[shotwell/wip/async-publishing] Convert Piwigo to async HTTP operation
- From: Jens Georg <jensgeorg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [shotwell/wip/async-publishing] Convert Piwigo to async HTTP operation
- Date: Thu, 8 Sep 2022 20:43:53 +0000 (UTC)
commit fea1e39c9308fe2fbe1ad9ef2b1c15143a6038b4
Author: Jens Georg <mail jensge org>
Date: Thu Sep 8 22:34:06 2022 +0200
Convert Piwigo to async HTTP operation
Instead of manually spinning the UI while doing sync libsoup
communication
plugins/common/RESTSupport.vala | 130 +++++++++++++++++--
plugins/shotwell-publishing/PiwigoPublishing.vala | 148 ++++++----------------
2 files changed, 157 insertions(+), 121 deletions(-)
---
diff --git a/plugins/common/RESTSupport.vala b/plugins/common/RESTSupport.vala
index cddedf4a..71c88454 100644
--- a/plugins/common/RESTSupport.vala
+++ b/plugins/common/RESTSupport.vala
@@ -86,6 +86,19 @@ public abstract class Session {
notify_wire_message_unqueued(message);
}
+ public async void send_wire_message_async(Soup.Message message) {
+ if (are_transactions_stopped()) {
+ return;
+ }
+
+ try {
+ this.body = yield soup_session.send_and_read_async(message, GLib.Priority.DEFAULT, null);
+ } catch (Error error) {
+ debug ("Failed to send_and_read: %s", error.message);
+ this.transport_error = error;
+ }
+ }
+
public void set_insecure () {
this.insecure = true;
}
@@ -368,7 +381,22 @@ public class Transaction {
if (err != null)
throw err;
- }
+ }
+
+ protected async void send_async() throws Spit.Publishing.PublishingError {
+ var id = message.wrote_body_data.connect((message, chunk_size) => {
+ bytes_written = chunk_size;
+
+ chunk_transmitted(bytes_written, (uint)request_length);
+ });
+ message.accept_certificate.connect(on_accecpt_certificate);
+
+ yield parent_session.send_wire_message_async(message);
+ check_response(message);
+
+ message.disconnect(id);
+ message.accept_certificate.disconnect(on_accecpt_certificate);
+ }
public HttpMethod get_method() {
return HttpMethod.from_string(message.method);
@@ -421,17 +449,7 @@ public class Transaction {
return message.status_code;
}
- public virtual void execute() throws Spit.Publishing.PublishingError {
- // if a custom payload is being used, we don't need to peform the tasks that are necessary
- // to prepare a traditional key-value pair REST request; Instead (since we don't
- // know anything about the custom payload), we just put it on the wire and return
- if (use_custom_payload) {
- is_executed = true;
- send();
-
- return;
- }
-
+ private GLib.Uri? prepare_rest_message() {
// REST POST requests must transmit at least one argument
if (get_method() == HttpMethod.POST)
assert(arguments.length > 0);
@@ -463,6 +481,45 @@ public class Transaction {
is_executed = true;
+ return old_url;
+ }
+
+ public virtual async void execute_async() throws Spit.Publishing.PublishingError {
+ // if a custom payload is being used, we don't need to peform the tasks that are necessary
+ // to prepare a traditional key-value pair REST request; Instead (since we don't
+ // know anything about the custom payload), we just put it on the wire and return
+ if (use_custom_payload) {
+ is_executed = true;
+ yield send_async();
+
+ return;
+ }
+
+ var old_url = prepare_rest_message();
+
+ try {
+ debug("sending message to URI = '%s'", message.get_uri().to_string());
+ yield send_async();
+ } finally {
+ // if old_url is non-null, then restore it
+ if (old_url != null)
+ message.set_uri(old_url);
+ }
+ }
+
+ public virtual void execute() throws Spit.Publishing.PublishingError {
+ // if a custom payload is being used, we don't need to peform the tasks that are necessary
+ // to prepare a traditional key-value pair REST request; Instead (since we don't
+ // know anything about the custom payload), we just put it on the wire and return
+ if (use_custom_payload) {
+ is_executed = true;
+ send();
+
+ return;
+ }
+
+ var old_url = prepare_rest_message();
+
try {
debug("sending message to URI = '%s'", message.get_uri().to_string());
send();
@@ -567,7 +624,7 @@ public class UploadTransaction : Transaction {
binary_disposition_table = new_disp_table;
}
- public override void execute() throws Spit.Publishing.PublishingError {
+ private void prepare_execution() throws Spit.Publishing.PublishingError {
Argument[] request_arguments = get_arguments();
assert(request_arguments.length > 0);
@@ -605,8 +662,16 @@ public class UploadTransaction : Transaction {
set_message(outbound_message, mapped_file.get_length());
set_is_executed(true);
+ }
+ public override void execute() throws Spit.Publishing.PublishingError {
+ prepare_execution();
send();
}
+
+ public override async void execute_async() throws Spit.Publishing.PublishingError {
+ prepare_execution();
+ yield send_async();
+ }
}
public class XmlDocument {
@@ -771,7 +836,35 @@ public abstract class BatchUploader {
if (!stop)
upload_complete(current_file);
}
-
+
+ private async void send_files_async() throws Spit.Publishing.PublishingError {
+ current_file = 0;
+ bool stop = false;
+ foreach (Spit.Publishing.Publishable publishable in publishables) {
+ GLib.File? file = publishable.get_serialized_file();
+
+ // if the current publishable hasn't been serialized, then skip it
+ if (file == null) {
+ current_file++;
+ continue;
+ }
+
+ double fraction_complete = ((double) current_file) / publishables.length;
+ if (status_updated != null)
+ status_updated(current_file + 1, fraction_complete);
+
+ Transaction txn = create_transaction(publishables[current_file]);
+
+ txn.chunk_transmitted.connect(on_chunk_transmitted);
+
+ yield txn.execute_async();
+
+ txn.chunk_transmitted.disconnect(on_chunk_transmitted);
+
+ current_file++;
+ }
+ }
+
private void on_chunk_transmitted(uint bytes_written_so_far, uint total_bytes) {
double file_span = 1.0 / publishables.length;
double this_file_fraction_complete = ((double) bytes_written_so_far) / total_bytes;
@@ -798,6 +891,15 @@ public abstract class BatchUploader {
if (publishables.length > 0)
send_files();
}
+
+ public async int upload_async(Spit.Publishing.ProgressCallback? status_updated = null) throws
Spit.Publishing.PublishingError {
+ this.status_updated = status_updated;
+
+ if (publishables.length > 0)
+ yield send_files_async();
+
+ return current_file;
+ }
}
// Remove diacritics in a string, yielding ASCII. If the given string is in
diff --git a/plugins/shotwell-publishing/PiwigoPublishing.vala
b/plugins/shotwell-publishing/PiwigoPublishing.vala
index acfb3f3f..3d94b1da 100644
--- a/plugins/shotwell-publishing/PiwigoPublishing.vala
+++ b/plugins/shotwell-publishing/PiwigoPublishing.vala
@@ -173,7 +173,7 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
if (session.is_authenticated()) {
debug("PiwigoPublisher: session is authenticated.");
- do_fetch_categories();
+ do_fetch_categories.begin();
} else {
debug("PiwigoPublisher: session is not authenticated.");
string? persistent_url = get_persistent_url();
@@ -182,7 +182,7 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
// This will only be null if either of the other two was null or the password did not exist
if (persistent_url != null && persistent_username != null && persistent_password != null)
- do_network_login(persistent_url, persistent_username,
+ do_network_login.begin(persistent_url, persistent_username,
persistent_password, get_remember_password());
else
do_show_authentication_pane();
@@ -356,7 +356,7 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
string? persistent_username = get_persistent_username();
string? persistent_password = get_persistent_password(persistent_url, persistent_username);
if (persistent_url != null && persistent_username != null && persistent_password != null)
- do_network_login(persistent_url, persistent_username,
+ do_network_login.begin(persistent_url, persistent_username,
persistent_password, get_remember_password());
else
do_show_authentication_pane();
@@ -384,7 +384,7 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
if (!running)
return;
- do_network_login(url, username, password, remember_password);
+ do_network_login.begin(url, username, password, remember_password);
}
/**
@@ -398,7 +398,7 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
* @param username the name of the Piwigo user used to login
* @param password the password of the Piwigo user used to login
*/
- private void do_network_login(string url, string username, string password, bool remember_password) {
+ private async void do_network_login(string url, string username, string password, bool
remember_password) {
debug("ACTION: logging in");
host.set_service_locked(true);
host.install_login_wait_pane();
@@ -412,11 +412,10 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
SessionLoginTransaction login_trans = new SessionLoginTransaction(
session, normalise_url(url), username, password);
- login_trans.network_error.connect(on_login_network_error);
- login_trans.completed.connect(on_login_network_complete);
try {
- login_trans.execute();
+ yield login_trans.execute_async();
+ on_login_network_complete(login_trans);
} catch (Spit.Publishing.PublishingError err) {
if (err is Spit.Publishing.PublishingError.SSL_FAILED) {
debug ("ERROR: SSL connection problems");
@@ -490,7 +489,7 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
debug("Setting session pwg_id to %s", pwg_id);
session.set_pwg_id(pwg_id);
- do_fetch_session_status(endpoint_url, pwg_id);
+ do_fetch_session_status.begin(endpoint_url, pwg_id);
}
/**
@@ -528,18 +527,17 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
* persisted. In this case, it will log the user in and confirm the
* identity.
*/
- private void do_fetch_session_status(string url = "", string pwg_id = "") {
+ private async void do_fetch_session_status(string url = "", string pwg_id = "") {
debug("ACTION: fetching session status");
host.set_service_locked(true);
host.install_account_fetch_wait_pane();
if (!session.is_authenticated()) {
SessionGetStatusTransaction status_txn = new
SessionGetStatusTransaction.unauthenticated(session, url, pwg_id);
- status_txn.network_error.connect(on_session_get_status_error);
- status_txn.completed.connect(on_session_get_status_complete);
try {
- status_txn.execute();
+ yield status_txn.execute_async();
+ on_session_get_status_complete(status_txn);
} catch (Spit.Publishing.PublishingError err) {
debug("ERROR: do_fetch_session_status, not authenticated");
do_show_error(err);
@@ -550,7 +548,8 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
status_txn.completed.connect(on_session_get_status_complete);
try {
- status_txn.execute();
+ yield status_txn.execute_async();
+ on_session_get_status_complete(status_txn);
} catch (Spit.Publishing.PublishingError err) {
debug("ERROR: do_fetch_session_status, authenticated");
do_show_error(err);
@@ -567,8 +566,6 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
*/
private void on_session_get_status_complete(Publishing.RESTSupport.Transaction txn) {
debug("EVENT: on_session_get_status_complete");
- txn.completed.disconnect(on_session_get_status_complete);
- txn.network_error.disconnect(on_session_get_status_error);
if (!session.is_authenticated()) {
string endpoint_url = txn.get_endpoint_url();
@@ -588,7 +585,7 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
session.authenticate(endpoint_url, username, pwg_id);
set_persistent_url(session.get_pwg_url());
set_persistent_username(session.get_username());
- do_fetch_categories();
+ do_fetch_categories.begin();
} catch (Spit.Publishing.PublishingError err2) {
debug("ERROR: on_session_get_status_complete, inner");
do_show_error(err2);
@@ -603,7 +600,7 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
// This should never happen as the session should not be
// authenticated at that point so this call is a safeguard
// against the interaction not happening properly.
- do_fetch_categories();
+ do_fetch_categories.begin();
}
}
@@ -626,20 +623,20 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
* This action fetches all categories from the Piwigo service in order
* to populate the publishing pane presented to the user.
*/
- private void do_fetch_categories() {
+ private async void do_fetch_categories() {
debug("ACTION: fetching categories");
host.set_service_locked(true);
host.install_account_fetch_wait_pane();
CategoriesGetListTransaction cat_trans = new CategoriesGetListTransaction(session);
- cat_trans.network_error.connect(on_category_fetch_error);
- cat_trans.completed.connect(on_category_fetch_complete);
try {
- cat_trans.execute();
+ yield cat_trans.execute_async();
+ on_category_fetch_complete(cat_trans);
} catch (Spit.Publishing.PublishingError err) {
debug("ERROR: do_fetch_categories");
do_show_error(err);
+ return;
}
}
@@ -652,8 +649,6 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
*/
private void on_category_fetch_complete(Publishing.RESTSupport.Transaction txn) {
debug("EVENT: on_category_fetch_complete");
- txn.completed.disconnect(on_category_fetch_complete);
- txn.network_error.disconnect(on_category_fetch_error);
debug("PiwigoConnector: list of categories: %s", txn.get_response());
// Empty the categories
if (categories != null) {
@@ -708,20 +703,6 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
do_show_publishing_options_pane();
}
- /**
- * Event triggered when the fetch categories transaction fails due to a
- * network error.
- */
- private void on_category_fetch_error(
- Publishing.RESTSupport.Transaction bad_txn,
- Spit.Publishing.PublishingError err
- ) {
- debug("EVENT: on_category_fetch_error");
- bad_txn.completed.disconnect(on_category_fetch_complete);
- bad_txn.network_error.disconnect(on_category_fetch_error);
- on_network_error(bad_txn, err);
- }
-
/**
* Action that shows the publishing options pane.
*
@@ -734,7 +715,7 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
PublishingOptionsPane opts_pane = new PublishingOptionsPane(
this, categories, get_last_category(), get_last_permission_level(), get_last_photo_size(),
get_last_title_as_comment(), get_last_no_upload_tags(), get_last_no_upload_ratings(),
get_metadata_removal_choice());
- opts_pane.logout.connect(on_publishing_options_pane_logout_clicked);
+ opts_pane.logout.connect(() => { on_publishing_options_pane_logout_clicked.begin(); });
opts_pane.publish.connect(on_publishing_options_pane_publish_clicked);
host.install_dialog_pane(opts_pane, Spit.Publishing.PluginHost.ButtonMode.CLOSE);
host.set_dialog_default_widget(opts_pane.get_default_widget());
@@ -743,14 +724,12 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
/**
* Event triggered when the user clicks logout in the publishing options pane.
*/
- private void on_publishing_options_pane_logout_clicked() {
+ private async void on_publishing_options_pane_logout_clicked() {
debug("EVENT: on_publishing_options_pane_logout_clicked");
- SessionLogoutTransaction logout_trans = new SessionLogoutTransaction(session);
- logout_trans.network_error.connect(on_logout_network_error);
- logout_trans.completed.connect(on_logout_network_complete);
try {
- logout_trans.execute();
+ yield new SessionLogoutTransaction(session).execute_async();
+ on_logout_network_complete();
} catch (Spit.Publishing.PublishingError err) {
debug("ERROR: on_publishing_options_pane_logout_clicked");
do_show_error(err);
@@ -763,29 +742,14 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
* This event de-authenticates the session and shows the authentication
* pane again.
*/
- private void on_logout_network_complete(Publishing.RESTSupport.Transaction txn) {
+ private void on_logout_network_complete() {
debug("EVENT: on_logout_network_complete");
- txn.completed.disconnect(on_logout_network_complete);
- txn.network_error.disconnect(on_logout_network_error);
session.deauthenticate();
do_show_authentication_pane(AuthenticationPane.Mode.INTRO);
}
-
- /**
- * Event triggered when the logout action fails due to a network error.
- */
- private void on_logout_network_error(
- Publishing.RESTSupport.Transaction bad_txn,
- Spit.Publishing.PublishingError err
- ) {
- debug("EVENT: on_logout_network_error");
- bad_txn.completed.disconnect(on_logout_network_complete);
- bad_txn.network_error.disconnect(on_logout_network_error);
- on_network_error(bad_txn, err);
- }
-
+
/**
* Event triggered when the user clicks publish in the publishing options pane.
*
@@ -803,9 +767,9 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
this.strip_metadata = strip_metadata;
if (parameters.category.is_local()) {
- do_create_category(parameters.category);
+ do_create_category.begin(parameters.category);
} else {
- do_upload(this.strip_metadata);
+ do_upload.begin(this.strip_metadata);
}
}
@@ -820,7 +784,7 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
*
* @param category the new category to create on the server
*/
- private void do_create_category(Category category) {
+ private async void do_create_category(Category category) {
debug("ACTION: creating a new category: %s".printf(category.name));
assert(category.is_local());
@@ -829,11 +793,10 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
CategoriesAddTransaction creation_trans = new CategoriesAddTransaction(
session, category.name.strip(), int.parse(category.uppercats), category.comment);
- creation_trans.network_error.connect(on_category_add_error);
- creation_trans.completed.connect(on_category_add_complete);
try {
- creation_trans.execute();
+ yield creation_trans.execute_async();
+ on_category_add_complete(creation_trans);
} catch (Spit.Publishing.PublishingError err) {
debug("ERROR: do_create_category");
do_show_error(err);
@@ -849,8 +812,6 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
*/
private void on_category_add_complete(Publishing.RESTSupport.Transaction txn) {
debug("EVENT: on_category_add_complete");
- txn.completed.disconnect(on_category_add_complete);
- txn.network_error.disconnect(on_category_add_error);
// Parse the response
try {
@@ -863,30 +824,17 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
string id_string = id_node->get_content();
int id = int.parse(id_string);
parameters.category.id = id;
- do_upload(strip_metadata);
+ do_upload.begin(strip_metadata);
} catch (Spit.Publishing.PublishingError err) {
debug("ERROR: on_category_add_complete");
do_show_error(err);
}
}
-
- /**
- * Event triggered when the add category action fails due to a network error.
- */
- private void on_category_add_error(
- Publishing.RESTSupport.Transaction bad_txn,
- Spit.Publishing.PublishingError err
- ) {
- debug("EVENT: on_category_add_error");
- bad_txn.completed.disconnect(on_category_add_complete);
- bad_txn.network_error.disconnect(on_category_add_error);
- on_network_error(bad_txn, err);
- }
-
+
/**
* Upload action: the big one, the one we've been waiting for!
*/
- private void do_upload(bool strip_metadata) {
+ private async void do_upload(bool strip_metadata) {
this.strip_metadata = strip_metadata;
debug("ACTION: uploading pictures");
@@ -904,19 +852,20 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
Spit.Publishing.Publishable[] publishables = host.get_publishables();
Uploader uploader = new Uploader(session, publishables, parameters);
- uploader.upload_complete.connect(on_upload_complete);
- uploader.upload_error.connect(on_upload_error);
- uploader.upload(on_upload_status_updated);
+ try {
+ var num_published = yield uploader.upload_async(on_upload_status_updated);
+ on_upload_complete(num_published);
+ } catch (Spit.Publishing.PublishingError err) {
+ do_show_error(err);
+ }
}
/**
* Event triggered when the batch uploader reports that at least one of the
* network transactions encapsulating uploads has completed successfully
*/
- private void on_upload_complete(Publishing.RESTSupport.BatchUploader uploader, int num_published) {
+ private void on_upload_complete(int num_published) {
debug("EVENT: on_upload_complete");
- uploader.upload_complete.disconnect(on_upload_complete);
- uploader.upload_error.disconnect(on_upload_error);
// TODO: should a message be displayed to the user if num_published is zero?
@@ -925,22 +874,7 @@ public class PiwigoPublisher : Spit.Publishing.Publisher, GLib.Object {
do_show_success_pane();
}
-
- /**
- * Event triggered when the batch uploader reports that at least one of the
- * network transactions encapsulating uploads has caused a network error
- */
- private void on_upload_error(
- Publishing.RESTSupport.BatchUploader uploader,
- Spit.Publishing.PublishingError err
- ) {
- debug("EVENT: on_upload_error");
- uploader.upload_complete.disconnect(on_upload_complete);
- uploader.upload_error.disconnect(on_upload_error);
-
- do_show_error(err);
- }
-
+
/**
* Event triggered when upload progresses and the status needs to be updated.
*/
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]