From bd7d6c0f67ccbf9314efdd4624c3d80d66dcfdca Mon Sep 17 00:00:00 2001 From: luboslenco Date: Wed, 19 Nov 2025 20:01:58 +0100 Subject: [PATCH] Make net request async on windows --- base/sources/backends/android_net.c | 5 +- base/sources/backends/apple_net.m | 7 +- base/sources/backends/posix_net.c | 5 +- base/sources/backends/windows_net.c | 252 +++++++++++++++++++++------- base/sources/iron.h | 5 +- base/sources/iron_net.h | 10 +- base/sources/ts/file.ts | 13 +- base/sources/ts/iron.ts | 2 +- paint/sources/box_preferences.ts | 35 +++- paint/sources/translator.ts | 2 +- 10 files changed, 247 insertions(+), 89 deletions(-) diff --git a/base/sources/backends/android_net.c b/base/sources/backends/android_net.c index 1a4638fb..fe862ecd 100644 --- a/base/sources/backends/android_net.c +++ b/base/sources/backends/android_net.c @@ -8,7 +8,7 @@ ANativeActivity *iron_android_get_activity(void); jclass iron_android_find_class(JNIEnv *env, const char *name); -void iron_https_request(const char *url_base, const char *url_path, const char *data, int port, int method, iron_http_callback_t callback, void *callbackdata) { +void iron_net_request(const char *url_base, const char *url_path, const char *data, int port, int method, iron_https_callback_t callback, void *callbackdata, const char *dst_path) { ANativeActivity *activity = iron_android_get_activity(); JNIEnv *env; JavaVM *vm = iron_android_get_activity()->vm; @@ -36,3 +36,6 @@ void iron_https_request(const char *url_base, const char *url_path, const char * (*vm)->DetachCurrentThread(vm); } + +void iron_net_update() { +} diff --git a/base/sources/backends/apple_net.m b/base/sources/backends/apple_net.m index a78e551e..9bbf38c6 100644 --- a/base/sources/backends/apple_net.m +++ b/base/sources/backends/apple_net.m @@ -4,7 +4,7 @@ static NSURLSession *session = nil; -void iron_https_request(const char *url_base, const char *url_path, const char *data, int port, int method, iron_http_callback_t callback, void *callbackdata) { +void iron_net_request(const char *url_base, const char *url_path, const char *data, int port, int method, iron_https_callback_t callback, void *callbackdata, const char *dst_path) { if (session == nil) { NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; session = [NSURLSession sessionWithConfiguration:sessionConfiguration]; @@ -18,7 +18,7 @@ void iron_https_request(const char *url_base, const char *url_path, const char * urlstring = [urlstring stringByAppendingString:[NSString stringWithUTF8String:url_path]]; NSURL *aUrl = [NSURL URLWithString:urlstring]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:aUrl]; - request.HTTPMethod = method == IRON_HTTP_GET ? @"GET" : @"POST"; + request.HTTPMethod = method == IRON_HTTPS_GET ? @"GET" : @"POST"; if (data != 0) { NSString *datastring = [NSString stringWithUTF8String:data]; request.HTTPBody = [datastring dataUsingEncoding:NSUTF8StringEncoding]; @@ -39,3 +39,6 @@ void iron_https_request(const char *url_base, const char *url_path, const char * }]; [dataTask resume]; } + +void iron_net_update() { +} diff --git a/base/sources/backends/posix_net.c b/base/sources/backends/posix_net.c index f594e8d8..ed2782b1 100644 --- a/base/sources/backends/posix_net.c +++ b/base/sources/backends/posix_net.c @@ -11,7 +11,7 @@ static SSL_CTX *ctx = NULL; static char *buf = NULL; static int buf_len = 1024 * 1024; -void iron_https_request(const char *url_base, const char *url_path, const char *data, int port, int method, iron_http_callback_t callback, void *callbackdata) { +void iron_net_request(const char *url_base, const char *url_path, const char *data, int port, int method, iron_https_callback_t callback, void *callbackdata, const char *dst_path) { int sock_fd = socket(AF_INET, SOCK_STREAM, 0); struct hostent *server = gethostbyname(url_base); @@ -69,3 +69,6 @@ void iron_https_request(const char *url_base, const char *url_path, const char * SSL_free(ssl); close(sock_fd); } + +void iron_net_update() { +} diff --git a/base/sources/backends/windows_net.c b/base/sources/backends/windows_net.c index 44b9ad27..569a2084 100644 --- a/base/sources/backends/windows_net.c +++ b/base/sources/backends/windows_net.c @@ -5,81 +5,213 @@ #include #include -static char *returnData = NULL; -static int returnDataSize = 0; +typedef struct { + HINTERNET hsession; + HINTERNET hconnect; + HINTERNET hrequest; + HANDLE hfile; + char *return_data; + int return_data_size; + int return_data_index; + char *file_buffer; + DWORD file_buffer_size; + iron_https_callback_t callback; + void *callbackdata; +} async_context_t; -void iron_https_request(const char *url_base, const char *url_path, const char *data, int port, int method, iron_http_callback_t callback, void *callbackdata) { - // based on https://docs.microsoft.com/en-us/windows/desktop/winhttp/winhttp-sessions-overview +typedef struct request { + char *response; + iron_https_callback_t callback; + void *callbackdata; + async_context_t *ctx; + struct request *next; +} request_t; - HINTERNET hSession = WinHttpOpen(L"WinHTTP via Iron/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); +static BOOL initialized = FALSE; +static request_t *pending_head = NULL; +static request_t *pending_tail = NULL; +static CRITICAL_SECTION cs; - HINTERNET hConnect = NULL; - if (hSession) { - wchar_t wurl[4096]; - MultiByteToWideChar(CP_UTF8, 0, url_base, -1, wurl, 4096); - hConnect = WinHttpConnect(hSession, wurl, port, 0); +static void finish_request(async_context_t *ctx, BOOL success) { + if (ctx->hfile != INVALID_HANDLE_VALUE) { + FlushFileBuffers(ctx->hfile); + CloseHandle(ctx->hfile); + ctx->hfile = INVALID_HANDLE_VALUE; } - HINTERNET hRequest = NULL; - if (hConnect) { - wchar_t wurl_path[4096]; - MultiByteToWideChar(CP_UTF8, 0, url_path, -1, wurl_path, 4096); - hRequest = WinHttpOpenRequest(hConnect, method == IRON_HTTP_GET ? L"GET" : L"POST", wurl_path, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, - WINHTTP_FLAG_SECURE); + char *response = NULL; + if (ctx->return_data) { + ctx->return_data[ctx->return_data_index] = '\0'; + response = ctx->return_data; + ctx->return_data = NULL; } - BOOL bResults = FALSE; - if (hRequest) { - DWORD optionalLength = (data != 0 && strlen(data) > 0) ? (DWORD)strlen(data) : 0; - bResults = WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, data == 0 ? WINHTTP_NO_REQUEST_DATA : (LPVOID)data, optionalLength, - optionalLength, 0); - } + request_t *req = malloc(sizeof(request_t)); + req->response = response; + req->callback = ctx->callback; + req->callbackdata = ctx->callbackdata; + req->ctx = ctx; + req->next = NULL; - if (bResults) { - bResults = WinHttpReceiveResponse(hRequest, NULL); - } - - int returnDataIndex = 0; - if (bResults) { - DWORD dwSize; - do { - dwSize = 0; - if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) { - iron_error("Error %d in WinHttpQueryDataAvailable.\n", GetLastError()); - } - - if ((int)dwSize + 1 > returnDataSize - returnDataIndex) { - returnDataSize = (returnDataIndex + dwSize + 1) * 2; - returnData = realloc(returnData, returnDataSize); - } - - DWORD dwDownloaded = 0; - if (!WinHttpReadData(hRequest, (LPVOID)(&returnData[returnDataIndex]), dwSize, &dwDownloaded)) { - iron_error("Error %d in WinHttpReadData.\n", GetLastError()); - } - returnDataIndex += dwSize; - } while (dwSize > 0); + EnterCriticalSection(&cs); + if (pending_tail == NULL) { + pending_head = pending_tail = req; } else { - callback(NULL, callbackdata); + pending_tail->next = req; + pending_tail = req; + } + LeaveCriticalSection(&cs); +} + +static void CALLBACK iron_winhttp_callback(HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus, LPVOID lpvStatusInformation, + DWORD dwStatusInformationLength) { + async_context_t *ctx = (async_context_t *)dwContext; + + switch (dwInternetStatus) { + case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE: + WinHttpReceiveResponse(ctx->hrequest, NULL); + break; + + case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE: + WinHttpQueryDataAvailable(ctx->hrequest, NULL); + break; + + case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE: { + DWORD dwSize = *(DWORD *)lpvStatusInformation; + if (dwSize == 0) { + finish_request(ctx, TRUE); + } + else { + if (ctx->hfile != INVALID_HANDLE_VALUE) { + if (dwSize > ctx->file_buffer_size) { + ctx->file_buffer = realloc(ctx->file_buffer, dwSize); + ctx->file_buffer_size = dwSize; + } + WinHttpReadData(ctx->hrequest, ctx->file_buffer, dwSize, NULL); + } + else { + if (ctx->return_data_index + (int)dwSize + 1 > ctx->return_data_size) { + ctx->return_data_size = (ctx->return_data_index + (int)dwSize + 1) * 2; + ctx->return_data = realloc(ctx->return_data, ctx->return_data_size); + } + WinHttpReadData(ctx->hrequest, ctx->return_data + ctx->return_data_index, dwSize, NULL); + } + } + break; + } + + case WINHTTP_CALLBACK_STATUS_READ_COMPLETE: + if (dwStatusInformationLength != 0) { + if (ctx->hfile != INVALID_HANDLE_VALUE) { + DWORD written; + WriteFile(ctx->hfile, ctx->file_buffer, dwStatusInformationLength, &written, NULL); + } + else { + ctx->return_data_index += dwStatusInformationLength; + } + } + WinHttpQueryDataAvailable(ctx->hrequest, NULL); + break; + + case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR: + finish_request(ctx, FALSE); + break; + } +} + +void iron_net_request(const char *url_base, const char *url_path, const char *data, int port, int method, iron_https_callback_t callback, void *callbackdata, + const char *dst_path) { + if (!initialized) { + InitializeCriticalSection(&cs); + initialized = TRUE; + } + + async_context_t *ctx = calloc(1, sizeof(async_context_t)); + ctx->callback = callback; + ctx->callbackdata = callbackdata; + ctx->hfile = INVALID_HANDLE_VALUE; + + if (dst_path) { + wchar_t wpath[1024]; + MultiByteToWideChar(CP_UTF8, 0, dst_path, -1, wpath, 1024); + ctx->hfile = CreateFileW(wpath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (ctx->hfile == INVALID_HANDLE_VALUE) { + finish_request(ctx, FALSE); + return; + } + } + + ctx->hsession = WinHttpOpen(L"WinHTTP via Iron/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, WINHTTP_FLAG_ASYNC); + if (!ctx->hsession) { + finish_request(ctx, FALSE); return; } - returnData[returnDataIndex] = 0; - - if (!bResults) { - iron_error("Error %d has occurred.\n", GetLastError()); + wchar_t wurl[4096]; + MultiByteToWideChar(CP_UTF8, 0, url_base, -1, wurl, 4096); + ctx->hconnect = WinHttpConnect(ctx->hsession, wurl, port, 0); + if (!ctx->hconnect) { + finish_request(ctx, FALSE); + return; } - if (hRequest) { - WinHttpCloseHandle(hRequest); - } - if (hConnect) { - WinHttpCloseHandle(hConnect); - } - if (hSession) { - WinHttpCloseHandle(hSession); + wchar_t wurl_path[4096]; + MultiByteToWideChar(CP_UTF8, 0, url_path, -1, wurl_path, 4096); + ctx->hrequest = WinHttpOpenRequest(ctx->hconnect, method == IRON_HTTPS_GET ? L"GET" : L"POST", wurl_path, NULL, WINHTTP_NO_REFERER, + WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE); + if (!ctx->hrequest) { + finish_request(ctx, FALSE); + return; } - callback(returnData, callbackdata); + DWORD_PTR ctx_ptr = (DWORD_PTR)ctx; + if (!WinHttpSetOption(ctx->hrequest, WINHTTP_OPTION_CONTEXT_VALUE, &ctx_ptr, sizeof(DWORD_PTR))) { + finish_request(ctx, FALSE); + return; + } + + WINHTTP_STATUS_CALLBACK prev = WinHttpSetStatusCallback(ctx->hrequest, iron_winhttp_callback, WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0); + if (prev == WINHTTP_INVALID_STATUS_CALLBACK) { + finish_request(ctx, FALSE); + return; + } + + DWORD data_len = data != NULL ? (DWORD)strlen(data) : 0; + BOOL bresult = WinHttpSendRequest(ctx->hrequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, data == NULL ? WINHTTP_NO_REQUEST_DATA : (LPVOID)data, data_len, + data_len, (DWORD_PTR)ctx); + if (!bresult) { + finish_request(ctx, FALSE); + return; + } +} + +void iron_net_update() { + if (!initialized) { + return; + } + + request_t *current; + EnterCriticalSection(&cs); + current = pending_head; + pending_head = pending_tail = NULL; + LeaveCriticalSection(&cs); + while (current) { + request_t *next = current->next; + request_t *req = current; + req->callback(req->response, req->callbackdata); + if (req->response) + free(req->response); + if (req->ctx->hfile) + CloseHandle(req->ctx->hfile); + WinHttpSetStatusCallback(req->ctx->hrequest, NULL, 0, 0); + WinHttpCloseHandle(req->ctx->hrequest); + WinHttpCloseHandle(req->ctx->hconnect); + WinHttpCloseHandle(req->ctx->hsession); + if (req->ctx->file_buffer) + free(req->ctx->file_buffer); + free(req->ctx); + free(current); + current = next; + } } diff --git a/base/sources/iron.h b/base/sources/iron.h index e0f78a29..0a3f0985 100644 --- a/base/sources/iron.h +++ b/base/sources/iron.h @@ -391,6 +391,7 @@ void _update(void *data) { iron_a2_update(); #endif + iron_net_update(); iron_update(); if (ui_get_current()) ui_end_frame(); @@ -1348,7 +1349,7 @@ void _https_callback(const char *body, void *callback_data) { free(cbd); } -void iron_file_download(string_t *url, void (*callback)(char *, buffer_t *), i32 size) { +void iron_file_download(string_t *url, void (*callback)(char *, buffer_t *), i32 size, string_t *dst_path) { _callback_data_t *cbd = malloc(sizeof(_callback_data_t)); cbd->size = size; strcpy(cbd->url, url); @@ -1376,7 +1377,7 @@ void iron_file_download(string_t *url, void (*callback)(char *, buffer_t *), i32 } url_path[j] = 0; - iron_https_request(url_base, url_path, NULL, 443, 0, &_https_callback, cbd); + iron_net_request(url_base, url_path, NULL, 443, IRON_HTTPS_GET, &_https_callback, cbd, dst_path); } bool _save_and_quit_callback_internal(); diff --git a/base/sources/iron_net.h b/base/sources/iron_net.h index 97abf2a7..9ca94804 100644 --- a/base/sources/iron_net.h +++ b/base/sources/iron_net.h @@ -3,9 +3,11 @@ #include #include -#define IRON_HTTP_GET 0 -#define IRON_HTTP_POST 1 +#define IRON_HTTPS_GET 0 +#define IRON_HTTPS_POST 1 -typedef void (*iron_http_callback_t)(const char *body, void *callbackdata); +typedef void (*iron_https_callback_t)(const char *body, void *callbackdata); -void iron_https_request(const char *url_base, const char *url_path, const char *data, int port, int method, iron_http_callback_t callback, void *callbackdata); +void iron_net_request(const char *url_base, const char *url_path, const char *data, int port, int method, iron_https_callback_t callback, void *callbackdata, const char *dst_path); + +void iron_net_update(); diff --git a/base/sources/ts/file.ts b/base/sources/ts/file.ts index 70b65650..28d21e32 100644 --- a/base/sources/ts/file.ts +++ b/base/sources/ts/file.ts @@ -1,6 +1,6 @@ type file_download_data_t = { - path: string; done : (url: string, ab: buffer_t) => void; + done : (url: string) => void; }; type file_cache_cloud_data_t = { @@ -84,16 +84,13 @@ function file_start(path: string) { /// end } -function file_download_to(url: string, dst_path: string, done: (url: string, ab: buffer_t) => void, size: i32 = 0) { - let fdd: file_download_data_t = {path : dst_path, done : done}; +function file_download_to(url: string, dst_path: string, done: (url: string) => void, size: i32 = 0) { + let fdd: file_download_data_t = {done : done}; map_set(_file_download_map, url, fdd); iron_file_download(url, function(url: string, ab: buffer_t) { let fdd: file_download_data_t = map_get(_file_download_map, url); - if (ab != null) { - iron_file_save_bytes(fdd.path, ab, 0); - } - fdd.done(url, ab); - }, size); + fdd.done(url); + }, size, dst_path); } function file_cache_cloud(path: string, done: (s: string) => void) { diff --git a/base/sources/ts/iron.ts b/base/sources/ts/iron.ts index 1860cb80..6e8e1d56 100644 --- a/base/sources/ts/iron.ts +++ b/base/sources/ts/iron.ts @@ -286,7 +286,7 @@ declare function iron_internal_save_path(): string; declare function iron_get_arg_count(): i32; declare function iron_get_arg(index: i32): string; declare function iron_internal_files_location(): string; -declare function iron_file_download(url: string, callback: (url: string, _: buffer_t) => void, size: i32 = 0): void; +declare function iron_file_download(url: string, callback: (url: string, _: buffer_t) => void, size: i32 = 0, dst_path: string = null): void; declare function draw_init(image_vert: buffer_t, image_frag: buffer_t, image_transform_vert: buffer_t, image_transform_frag: buffer_t, rect_vert: buffer_t, rect_frag: buffer_t, tris_vert: buffer_t, tris_frag: buffer_t, text_vert: buffer_t, text_frag: buffer_t): void; diff --git a/paint/sources/box_preferences.ts b/paint/sources/box_preferences.ts index e7dc4028..57d2cd9b 100644 --- a/paint/sources/box_preferences.ts +++ b/paint/sources/box_preferences.ts @@ -707,10 +707,19 @@ function box_preferences_neural_tab() { ui_text(tr("Models")); if (ui_panel(ui_handle(__ID__), "Stable Diffusion")) { // (downloaded) - ui_text("source: https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5 (openrail)"); + if (ui_text("source: https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5 (openrail)") == UI_STATE_RELEASED) { + iron_load_url("https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5"); + } ui_text("gpu memory: 4GB"); ui_text("nodes: Inpaint Image, Outpaint Image, Text to Image, Tile Image, Vary Image"); if (ui_button(tr("Download (4.0GB)"))) { + + if (file_read_directory(neural_node_dir())[0] == "") { + file_create_directory(neural_node_dir()); + } + + file_download_to("https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5/resolve/main/v1-5-pruned-emaonly.safetensors", neural_node_dir() + path_sep + "v1-5-pruned-emaonly.safetensors", function(url: string) {}); + // https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5/resolve/main/v1-5-pruned-emaonly.safetensors } } @@ -720,14 +729,14 @@ function box_preferences_neural_tab() { // ui_text("gpu memory: 4GB"); // ui_text("nodes: Inpaint Image, Outpaint Image, Tile Image, Vary Image"); // if (ui_button(tr("Download (4.0GB)"))) { - // // https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5/resolve/main/v1-5-pruned-emaonly.safetensors + // // https://huggingface.co/webui/stable-diffusion-inpainting/resolve/main/sd-v1-5-inpainting.safetensors // } // } - // https://huggingface.co/webui/stable-diffusion-inpainting/resolve/main/sd-v1-5-inpainting.safetensors - if (ui_panel(ui_handle(__ID__), "Qwen Image")) { // (downloaded) - ui_text("source: https://huggingface.co/QuantStack/Qwen-Image-GGUF (apache-2.0)"); + if (ui_text("source: https://huggingface.co/QuantStack/Qwen-Image-GGUF (apache-2.0)") == UI_STATE_RELEASED) { + iron_load_url("https://huggingface.co/QuantStack/Qwen-Image-GGUF"); + } ui_text("gpu memory: 13GB"); ui_text("nodes: Text to Image"); if (ui_button(tr("Download") + " (15.7GB)")) { @@ -740,7 +749,9 @@ function box_preferences_neural_tab() { } if (ui_panel(ui_handle(__ID__), "Qwen Image Edit")) { // (downloaded) - ui_text("source: https://huggingface.co/QuantStack/Qwen-Image-Edit-2509-GGUF (apache-2.0)"); + if (ui_text("source: https://huggingface.co/QuantStack/Qwen-Image-Edit-2509-GGUF (apache-2.0)") == UI_STATE_RELEASED) { + iron_load_url("https://huggingface.co/QuantStack/Qwen-Image-Edit-2509-GGUF"); + } ui_text("gpu memory: 13GB"); ui_text("nodes: Edit Image, Inpaint Image, Outpaint Image, Tile Image, Vary Image"); if (ui_button(tr("Download (17.0GB)"))) { @@ -752,7 +763,9 @@ function box_preferences_neural_tab() { } if (ui_panel(ui_handle(__ID__), "Wan")) { // (downloaded) - ui_text("source: https://huggingface.co/QuantStack/Wan2.2-T2V-A14B-GGUF (apache-2.0)"); + if (ui_text("source: https://huggingface.co/QuantStack/Wan2.2-T2V-A14B-GGUF (apache-2.0)") == UI_STATE_RELEASED) { + iron_load_url("https://huggingface.co/QuantStack/Wan2.2-T2V-A14B-GGUF"); + } ui_text("gpu memory: 10GB"); ui_text("nodes: Text to Image"); if (ui_button(tr("Download (19.8GB)"))) { @@ -764,7 +777,9 @@ function box_preferences_neural_tab() { } if (ui_panel(ui_handle(__ID__), "Marigold")) { // (downloaded) - ui_text("source: https://huggingface.co/armory3d/marigold-v1-1-gguf (openrail)"); + if (ui_text("source: https://huggingface.co/armory3d/marigold-v1-1-gguf (openrail)") == UI_STATE_RELEASED) { + iron_load_url("https://huggingface.co/armory3d/marigold-v1-1-gguf"); + } ui_text("gpu memory: 6GB"); ui_text("nodes: Image to Depth, Image to Normal Map, Image to PBR"); if (ui_button(tr("Download (9.6GB)"))) { @@ -776,7 +791,9 @@ function box_preferences_neural_tab() { } if (ui_panel(ui_handle(__ID__), "RealESRGAN")) { // (downloaded) - ui_text("source: https://huggingface.co/armory3d/Real-ESRGAN (bsd-3-clause)"); + if (ui_text("source: https://huggingface.co/armory3d/Real-ESRGAN (bsd-3-clause)") == UI_STATE_RELEASED) { + iron_load_url("https://huggingface.co/armory3d/Real-ESRGAN"); + } ui_text("gpu memory: 1GB"); ui_text("nodes: Upscale Image"); if (ui_button(tr("Download (0.06GB)"))) { diff --git a/paint/sources/translator.ts b/paint/sources/translator.ts index fefcf285..b4db9754 100644 --- a/paint/sources/translator.ts +++ b/paint/sources/translator.ts @@ -120,7 +120,7 @@ function translator_load_translations(new_locale: string) { if (!iron_file_exists(_translator_load_translations_cjk_font_disk_path)) { file_download_to("https://github.com/armory3d/armorbase/raw/main/Assets/common/extra/font_cjk.ttc", - _translator_load_translations_cjk_font_disk_path, function() { + _translator_load_translations_cjk_font_disk_path, function(url: string) { if (!iron_file_exists(_translator_load_translations_cjk_font_disk_path)) { // Fall back to English config_raw.locale = "en";