Download extraction in GTK+ Launcher.
This commit is contained in:
@@ -187,6 +187,7 @@ typedef struct download_t
|
|||||||
int total_bytes;
|
int total_bytes;
|
||||||
JSContextGroupRef ctx_group;
|
JSContextGroupRef ctx_group;
|
||||||
JSObjectRef download_progressed_func;
|
JSObjectRef download_progressed_func;
|
||||||
|
JSObjectRef extraction_progressed_func;
|
||||||
JSValueRef status;
|
JSValueRef status;
|
||||||
JSValueRef error;
|
JSValueRef error;
|
||||||
GIOChannel * output_channel;
|
GIOChannel * output_channel;
|
||||||
@@ -209,6 +210,24 @@ download_t * find_download(char const * key)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_download_status(JSContextRef ctx, download_t * download, const char * status)
|
||||||
|
{
|
||||||
|
JSValueUnprotect(ctx, download->status);
|
||||||
|
|
||||||
|
download->status = JSValueMakeString(ctx, JS_STR(status));
|
||||||
|
|
||||||
|
JSValueProtect(ctx, download->status);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_download_error(JSContextRef ctx, download_t * download, const char * error)
|
||||||
|
{
|
||||||
|
JSValueUnprotect(ctx, download->error);
|
||||||
|
|
||||||
|
download->error = JSValueMakeString(ctx, JS_STR(error));
|
||||||
|
|
||||||
|
JSValueProtect(ctx, download->error);
|
||||||
|
}
|
||||||
|
|
||||||
JSValueRef js_register_download(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
|
JSValueRef js_register_download(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
|
||||||
size_t argc, const JSValueRef argv[], JSValueRef * exception)
|
size_t argc, const JSValueRef argv[], JSValueRef * exception)
|
||||||
{
|
{
|
||||||
@@ -239,6 +258,8 @@ JSValueRef js_register_download(JSContextRef ctx, JSObjectRef func, JSObjectRef
|
|||||||
download->ctx_group = JSContextGetGroup(ctx);
|
download->ctx_group = JSContextGetGroup(ctx);
|
||||||
o = JSObjectGetProperty(ctx, JSContextGetGlobalObject(ctx), JS_STR("downloadProgressed"), NULL);
|
o = JSObjectGetProperty(ctx, JSContextGetGlobalObject(ctx), JS_STR("downloadProgressed"), NULL);
|
||||||
download->download_progressed_func = JSValueToObject(ctx, o, NULL);
|
download->download_progressed_func = JSValueToObject(ctx, o, NULL);
|
||||||
|
o = JSObjectGetProperty(ctx, JSContextGetGlobalObject(ctx), JS_STR("extractProgressed"), NULL);
|
||||||
|
download->extraction_progressed_func = JSValueToObject(ctx, o, NULL);
|
||||||
|
|
||||||
strncpy(download->key, key, 31);
|
strncpy(download->key, key, 31);
|
||||||
download->key[31] = '\0';
|
download->key[31] = '\0';
|
||||||
@@ -261,16 +282,17 @@ JSValueRef js_register_download(JSContextRef ctx, JSObjectRef func, JSObjectRef
|
|||||||
if (NULL != f)
|
if (NULL != f)
|
||||||
{
|
{
|
||||||
fclose(f);
|
fclose(f);
|
||||||
download->status = JSValueMakeString(ctx, JS_STR("DOWNLOADED"));
|
set_download_status(ctx, download, "DOWNLOADED");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
download->status = JSValueMakeString(ctx, JS_STR("AVAILABLE"));
|
set_download_status(ctx, download, "AVAILABLE");
|
||||||
|
|
||||||
return JSValueMakeNull(ctx);
|
return JSValueMakeNull(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean update_download_stats(GIOChannel * source, GIOCondition condition, gpointer data)
|
gboolean update_download_stats(GIOChannel * source, GIOCondition condition, gpointer data)
|
||||||
{
|
{
|
||||||
|
int ret = TRUE;
|
||||||
download_t * download = (download_t *)data;
|
download_t * download = (download_t *)data;
|
||||||
gchar * line;
|
gchar * line;
|
||||||
gsize line_length;
|
gsize line_length;
|
||||||
@@ -288,12 +310,12 @@ gboolean update_download_stats(GIOChannel * source, GIOCondition condition, gpoi
|
|||||||
{
|
{
|
||||||
if (0 == memcmp(line, "Error:", 6))
|
if (0 == memcmp(line, "Error:", 6))
|
||||||
{
|
{
|
||||||
download->status = JSValueMakeString(ctx, JS_STR("ERROR"));
|
set_download_status(ctx, download, "ERROR");
|
||||||
download->error = JSValueMakeString(ctx, JS_STR(line + 7));
|
set_download_error(ctx, download, line + 7);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
download->status = JSValueMakeString(ctx, JS_STR("DOWNLOADING"));
|
set_download_status(ctx, download, "DOWNLOADING");
|
||||||
GRegex * pattern = g_regex_new("(\\d{1,3})% (\\d+)/(\\d+) bytes", 0, 0, NULL);
|
GRegex * pattern = g_regex_new("(\\d{1,3})% (\\d+)/(\\d+) bytes", 0, 0, NULL);
|
||||||
GMatchInfo * match;
|
GMatchInfo * match;
|
||||||
if (g_regex_match(pattern, line, 0, &match))
|
if (g_regex_match(pattern, line, 0, &match))
|
||||||
@@ -311,8 +333,9 @@ gboolean update_download_stats(GIOChannel * source, GIOCondition condition, gpoi
|
|||||||
break;
|
break;
|
||||||
case G_IO_HUP:
|
case G_IO_HUP:
|
||||||
if (!JSStringIsEqualToUTF8CString(JSValueToStringCopy(ctx, download->status, NULL), "ERROR"))
|
if (!JSStringIsEqualToUTF8CString(JSValueToStringCopy(ctx, download->status, NULL), "ERROR"))
|
||||||
download->status = JSValueMakeString(ctx, JS_STR("DOWNLOADED"));
|
set_download_status(ctx, download, "DOWNLOADED");
|
||||||
g_io_channel_shutdown(source, FALSE, NULL);
|
g_io_channel_shutdown(source, FALSE, NULL);
|
||||||
|
ret = FALSE;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -321,8 +344,7 @@ gboolean update_download_stats(GIOChannel * source, GIOCondition condition, gpoi
|
|||||||
args[0] = JSValueMakeString(ctx, JS_STR(download->key));
|
args[0] = JSValueMakeString(ctx, JS_STR(download->key));
|
||||||
JSObjectCallAsFunction(ctx, download->download_progressed_func, NULL, 1, args, NULL);
|
JSObjectCallAsFunction(ctx, download->download_progressed_func, NULL, 1, args, NULL);
|
||||||
|
|
||||||
JSGlobalContextRelease((JSGlobalContextRef)ctx);
|
return ret;
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JSValueRef js_start_download(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
|
JSValueRef js_start_download(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
|
||||||
@@ -349,7 +371,7 @@ JSValueRef js_start_download(JSContextRef ctx, JSObjectRef func, JSObjectRef thi
|
|||||||
|
|
||||||
g_message("Starting download %s", download->key);
|
g_message("Starting download %s", download->key);
|
||||||
|
|
||||||
download->status = JSValueMakeString(ctx, JS_STR("DOWNLOADING"));
|
set_download_status(ctx, download, "DOWNLOADING");
|
||||||
|
|
||||||
fd = util_do_download(download->url, download->dest, &pid);
|
fd = util_do_download(download->url, download->dest, &pid);
|
||||||
|
|
||||||
@@ -384,8 +406,8 @@ JSValueRef js_cancel_download(JSContextRef ctx, JSObjectRef func, JSObjectRef th
|
|||||||
|
|
||||||
if (download->pid)
|
if (download->pid)
|
||||||
{
|
{
|
||||||
download->status = JSValueMakeString(ctx, JS_STR("ERROR"));
|
set_download_status(ctx, download, "ERROR");
|
||||||
download->error = JSValueMakeString(ctx, JS_STR("Download Cancelled"));
|
set_download_error(ctx, download, "Download Cancelled");
|
||||||
kill(download->pid, SIGTERM);
|
kill(download->pid, SIGTERM);
|
||||||
remove(download->dest);
|
remove(download->dest);
|
||||||
}
|
}
|
||||||
@@ -457,7 +479,7 @@ JSValueRef js_bytes_completed(JSContextRef ctx, JSObjectRef func, JSObjectRef th
|
|||||||
if (NULL == (download = find_download(key)))
|
if (NULL == (download = find_download(key)))
|
||||||
{
|
{
|
||||||
free(key);
|
free(key);
|
||||||
return JSValueMakeNumber(ctx, 0);
|
return JSValueMakeNumber(ctx, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(key);
|
free(key);
|
||||||
@@ -480,7 +502,7 @@ JSValueRef js_bytes_total(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
|
|||||||
if (NULL == (download = find_download(key)))
|
if (NULL == (download = find_download(key)))
|
||||||
{
|
{
|
||||||
free(key);
|
free(key);
|
||||||
return JSValueMakeNumber(ctx, 0);
|
return JSValueMakeNumber(ctx, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(key);
|
free(key);
|
||||||
@@ -488,10 +510,96 @@ JSValueRef js_bytes_total(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
|
|||||||
return JSValueMakeNumber(ctx, download->total_bytes);
|
return JSValueMakeNumber(ctx, download->total_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gboolean update_extraction_progress(GIOChannel * source, GIOCondition condition, gpointer data)
|
||||||
|
{
|
||||||
|
int ret = TRUE;
|
||||||
|
download_t * download = (download_t *)data;
|
||||||
|
gchar * line;
|
||||||
|
gsize line_length;
|
||||||
|
GIOStatus io_status;
|
||||||
|
JSValueRef args[1];
|
||||||
|
JSContextRef ctx;
|
||||||
|
|
||||||
|
ctx = JSGlobalContextCreateInGroup(download->ctx_group, NULL);
|
||||||
|
|
||||||
|
switch(condition)
|
||||||
|
{
|
||||||
|
case G_IO_IN:
|
||||||
|
io_status = g_io_channel_read_line(source, &line, &line_length, NULL, NULL);
|
||||||
|
if ((G_IO_STATUS_NORMAL == io_status) && (0 == memcmp(line, "Error:", 6)))
|
||||||
|
{
|
||||||
|
set_download_status(ctx, download, "ERROR");
|
||||||
|
set_download_error(ctx, download, line + 7);
|
||||||
|
}
|
||||||
|
free(line);
|
||||||
|
break;
|
||||||
|
case G_IO_HUP:
|
||||||
|
if (!JSStringIsEqualToUTF8CString(JSValueToStringCopy(ctx, download->status, NULL), "ERROR"))
|
||||||
|
set_download_status(ctx, download, "EXTRACTED");
|
||||||
|
g_io_channel_shutdown(source, FALSE, NULL);
|
||||||
|
ret = FALSE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
args[0] = JSValueMakeString(ctx, JS_STR(download->key));
|
||||||
|
JSObjectCallAsFunction(ctx, download->extraction_progressed_func, NULL, 1, args, NULL);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
JSValueRef js_extract_download(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
|
JSValueRef js_extract_download(JSContextRef ctx, JSObjectRef func, JSObjectRef this,
|
||||||
size_t argc, const JSValueRef argv[], JSValueRef * exception)
|
size_t argc, const JSValueRef argv[], JSValueRef * exception)
|
||||||
{
|
{
|
||||||
return JSValueMakeNull(ctx);
|
char * key, * dir, * mod, * status, dest_path[512];
|
||||||
|
size_t size;
|
||||||
|
download_t * download;
|
||||||
|
int fd;
|
||||||
|
GPid pid;
|
||||||
|
|
||||||
|
if (!js_check_num_args(ctx, "extractDownload", argc, 3, exception))
|
||||||
|
return JSValueMakeNull(ctx);
|
||||||
|
|
||||||
|
key = js_get_cstr_from_val(ctx, argv[0], &size);
|
||||||
|
|
||||||
|
if (NULL == (download = find_download(key)))
|
||||||
|
return JSValueMakeBoolean(ctx, 0);
|
||||||
|
|
||||||
|
status = js_get_cstr_from_val(ctx, download->status, &size);
|
||||||
|
|
||||||
|
if (0 != strcmp(status, "DOWNLOADED"))
|
||||||
|
return JSValueMakeBoolean(ctx, 0);
|
||||||
|
|
||||||
|
free(status);
|
||||||
|
|
||||||
|
set_download_status(ctx, download, "EXTRACTING");
|
||||||
|
|
||||||
|
dir = js_get_cstr_from_val(ctx, argv[1], &size);
|
||||||
|
mod = js_get_cstr_from_val(ctx, argv[2], &size);
|
||||||
|
|
||||||
|
sprintf(dest_path, "%s/%s", mod, dir);
|
||||||
|
|
||||||
|
fd = util_do_extract(download->dest, dest_path, &pid);
|
||||||
|
|
||||||
|
if (!fd)
|
||||||
|
{
|
||||||
|
free(key);
|
||||||
|
free(dir);
|
||||||
|
free(mod);
|
||||||
|
return JSValueMakeBoolean(ctx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
download->pid = pid;
|
||||||
|
download->output_channel = g_io_channel_unix_new(fd);
|
||||||
|
|
||||||
|
g_io_add_watch(download->output_channel, G_IO_IN | G_IO_HUP, update_extraction_progress, download);
|
||||||
|
|
||||||
|
free(key);
|
||||||
|
free(dir);
|
||||||
|
free(mod);
|
||||||
|
|
||||||
|
return JSValueMakeBoolean(ctx, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void js_add_functions(JSGlobalContextRef ctx, JSObjectRef target, char ** names,
|
void js_add_functions(JSGlobalContextRef ctx, JSObjectRef target, char ** names,
|
||||||
|
|||||||
@@ -87,30 +87,42 @@ int util_get_setting(const char * setting, GChildWatchFunc callback)
|
|||||||
return return_val;
|
return return_val;
|
||||||
}
|
}
|
||||||
|
|
||||||
int util_do_download(const char * url, const char * dest, GPid * pid)
|
int util_spawn_with_command(const char * command, const char * arg1, const char * arg2, GPid * pid)
|
||||||
{
|
{
|
||||||
char * command;
|
char * complete_command;
|
||||||
int out_fd;
|
int out_fd;
|
||||||
gboolean result;
|
gboolean result;
|
||||||
|
|
||||||
char * launch_args[] = { "mono", "OpenRA.Utility.exe", NULL, NULL };
|
char * launch_args[] = { "mono", "OpenRA.Utility.exe", NULL, NULL };
|
||||||
|
|
||||||
command = (char *)malloc(strlen(url) + strlen(dest) + strlen("--download-url=") + 2);
|
complete_command = (char *)malloc(strlen(command) + strlen(arg1) + strlen(arg2) + 2);
|
||||||
sprintf(command, "--download-url=%s,%s", url, dest);
|
sprintf(complete_command, "%s%s,%s", command, arg1, arg2);
|
||||||
|
|
||||||
launch_args[2] = command;
|
launch_args[2] = complete_command;
|
||||||
|
|
||||||
result = g_spawn_async_with_pipes(NULL, launch_args, NULL, G_SPAWN_SEARCH_PATH,
|
result = g_spawn_async_with_pipes(NULL, launch_args, NULL, G_SPAWN_SEARCH_PATH,
|
||||||
NULL, NULL, pid, NULL, &out_fd, NULL, NULL);
|
NULL, NULL, pid, NULL, &out_fd, NULL, NULL);
|
||||||
free(command);
|
|
||||||
|
free(complete_command);
|
||||||
|
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return out_fd;
|
return out_fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int util_do_download(const char * url, const char * dest, GPid * pid)
|
||||||
|
{
|
||||||
|
return util_spawn_with_command("--download-url=", url, dest, pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
int util_do_extract(const char * target, const char * dest, GPid * pid)
|
||||||
|
{
|
||||||
|
return util_spawn_with_command("--extract-zip=", target, dest, pid);
|
||||||
|
}
|
||||||
|
|
||||||
char * util_get_output(int fd, int * output_len)
|
char * util_get_output(int fd, int * output_len)
|
||||||
{
|
{
|
||||||
char buffer[1024], * msg = NULL;
|
char buffer[1024], * msg = NULL;
|
||||||
|
|||||||
@@ -10,4 +10,5 @@ int util_get_mod_list (GChildWatchFunc);
|
|||||||
int util_get_mod_metadata(char const *, GChildWatchFunc);
|
int util_get_mod_metadata(char const *, GChildWatchFunc);
|
||||||
int util_get_setting(const char *, GChildWatchFunc);
|
int util_get_setting(const char *, GChildWatchFunc);
|
||||||
int util_do_download(const char *, const char *, GPid *);
|
int util_do_download(const char *, const char *, GPid *);
|
||||||
|
int util_do_extract(const char *, const char *, GPid *);
|
||||||
char * util_get_output(int, int *);
|
char * util_get_output(int, int *);
|
||||||
|
|||||||
Reference in New Issue
Block a user