[shotwell/wip/phako/google-photos] Wip: upload



commit 81ba8e6dbcc5e3674fc1c7bc7d2d0331dfabf070
Author: Jens Georg <mail jensge org>
Date:   Mon Jan 21 23:20:32 2019 +0100

    Wip: upload

 data/gsettings/org.yorba.shotwell.gschema.xml      |  6 +-
 plugins/common/Resources.vala                      |  7 +-
 plugins/shotwell-publishing/PhotosPublisher.vala   | 64 +++++++++++++++++--
 .../shotwell-publishing/PhotosPublishingPane.vala  | 33 +++++++++-
 plugins/shotwell-publishing/PhotosService.vala     |  4 +-
 plugins/shotwell-publishing/PhotosUploader.vala    | 74 ++++++++++++++++++++++
 plugins/shotwell-publishing/meson.build            |  3 +-
 7 files changed, 174 insertions(+), 17 deletions(-)
---
diff --git a/data/gsettings/org.yorba.shotwell.gschema.xml b/data/gsettings/org.yorba.shotwell.gschema.xml
index 65955fa0..2c0893e0 100644
--- a/data/gsettings/org.yorba.shotwell.gschema.xml
+++ b/data/gsettings/org.yorba.shotwell.gschema.xml
@@ -413,7 +413,7 @@
     <child name="flickr" schema="org.yorba.shotwell.sharing.flickr" />
     <child name="gallery3" schema="org.yorba.shotwell.sharing.publishing-gallery3" />
     <child name="picasa" schema="org.yorba.shotwell.sharing.picasa" />
-    <child name="org-gnome-shotwell-publishing-photos" 
schema="org.yorba.shotwell.sharing.org-gnome-shotwell-publishing-photos" />
+    <child name="org-gnome-shotwell-publishing-google-photos" 
schema="org.yorba.shotwell.sharing.org-gnome-shotwell-publishing-google-photos" />
     <child name="youtube" schema="org.yorba.shotwell.sharing.youtube" />
 </schema>
 
@@ -513,7 +513,7 @@
     </key>
 </schema>
 
-<schema id="org.yorba.shotwell.sharing.org-gnome-shotwell-publishing-photos" 
path="/org/yorba/shotwell/sharing/org-gnome-shotwell-publishing-photos/">
+<schema id="org.yorba.shotwell.sharing.org-gnome-shotwell-publishing-google-photos" 
path="/org/yorba/shotwell/sharing/org-gnome-shotwell-publishing-google-photos/">
     <key name="refresh-token" type="s">
         <default>""</default>
         <summary>refresh token</summary>
@@ -754,7 +754,7 @@
         <description>True if the Picasa Web Albums publishing plugin is enabled, false 
otherwise</description>
     </key>
 
-    <key name="org-gnome-shotwell-publishing-photos" type="b">
+    <key name="org-gnome-shotwell-publishing-google-photos" type="b">
         <default>true</default>
         <summary>enable Google Photos publishing plugin</summary>
         <description>True if the Google Photos publishing plugin is enabled, false otherwise</description>
diff --git a/plugins/common/Resources.vala b/plugins/common/Resources.vala
index 29c7294e..ecbf2f87 100644
--- a/plugins/common/Resources.vala
+++ b/plugins/common/Resources.vala
@@ -42,7 +42,7 @@ public Gdk.Pixbuf[]? load_icon_set(GLib.File? icon_file) {
     try {
         icon = new Gdk.Pixbuf.from_file(icon_file.get_path());
     } catch (Error err) {
-        warning("couldn't load icon set from %s.", icon_file.get_path());
+        warning("couldn't load icon set from %s: %s", icon_file.get_path(), err.message);
     }
     
     if (icon != null) {
@@ -57,9 +57,10 @@ public Gdk.Pixbuf[]? load_icon_set(GLib.File? icon_file) {
 public Gdk.Pixbuf[]? load_from_resource (string resource_path) {
     Gdk.Pixbuf? icon = null;
     try {
-        icon = new Gdk.Pixbuf.from_resource (resource_path);
+        debug ("Loading icon from %s", resource_path);
+        icon = new Gdk.Pixbuf.from_resource_at_scale (resource_path, 24, 24, true);
     } catch (Error error) {
-        warning ("Couldn't load icon set from %s", resource_path);
+        warning ("Couldn't load icon set from %s: %s", resource_path, error.message);
     }
 
     if (icon != null) {
diff --git a/plugins/shotwell-publishing/PhotosPublisher.vala 
b/plugins/shotwell-publishing/PhotosPublisher.vala
index a3366b8c..11111f67 100644
--- a/plugins/shotwell-publishing/PhotosPublisher.vala
+++ b/plugins/shotwell-publishing/PhotosPublisher.vala
@@ -15,6 +15,7 @@ internal class PublishingParameters {
     public const int ORIGINAL_SIZE = -1;
     
     private string? target_album_name;
+    private string? target_album_id;
     private bool album_public;
     private bool strip_metadata;
     private int major_axis_size_pixels;
@@ -26,6 +27,7 @@ internal class PublishingParameters {
     public PublishingParameters() {
         this.user_name = "[unknown]";
         this.target_album_name = null;
+        this.target_album_id = null;
         this.major_axis_size_selection_id = 0;
         this.major_axis_size_pixels = ORIGINAL_SIZE;
         this.album_public = false;
@@ -41,6 +43,10 @@ internal class PublishingParameters {
     public void set_target_album_name(string target_album_name) {
         this.target_album_name = target_album_name;
     }
+
+    public void set_target_album_entry_id(string target_album_id) {
+        this.target_album_id = target_album_id;
+    }
     
     public string get_user_name() {
         return user_name;
@@ -58,14 +64,14 @@ internal class PublishingParameters {
         this.albums = albums;
     }
 
-    /*
+
     public void set_major_axis_size_pixels(int pixels) {
         this.major_axis_size_pixels = pixels;
     }
-        
+
     public int get_major_axis_size_pixels() {
         return major_axis_size_pixels;
-    } */
+    }
     
     public void set_major_axis_size_selection_id(int selection_id) {
         this.major_axis_size_selection_id = selection_id;
@@ -139,6 +145,7 @@ public class Publisher : Publishing.RESTSupport.GooglePublisher {
     private Spit.Publishing.Authenticator authenticator;
     private bool running = false;
     private PublishingParameters publishing_parameters;
+    private Spit.Publishing.ProgressCallback progress_reporter;
 
     public Publisher(Spit.Publishing.Service service,
                      Spit.Publishing.PluginHost host) {
@@ -227,13 +234,34 @@ public class Publisher : Publishing.RESTSupport.GooglePublisher {
         debug("ACTION: showing publishing options pane.");
 
         var opts_pane = new PublishingOptionsPane(this.publishing_parameters, 
this.authenticator.can_logout());
-//        opts_pane.publish.connect(on_publishing_options_publish);
-//        opts_pane.logout.connect(on_publishing_options_logout);
+        opts_pane.publish.connect(on_publishing_options_publish);
+        opts_pane.logout.connect(on_publishing_options_logout);
         get_host().install_dialog_pane(opts_pane);
 
         get_host().set_service_locked(false);
     }
 
+    private void on_publishing_options_logout() {
+        if (!is_running())
+            return;
+
+        debug("EVENT: user clicked 'Logout' in the publishing options pane.");
+
+        do_logout();
+    }
+
+    private void on_publishing_options_publish() {
+        if (!is_running())
+            return;
+
+        debug("EVENT: user clicked 'Publish' in the publishing options pane.");
+
+        save_parameters_to_configuration_system(publishing_parameters);
+
+        do_upload();
+    }
+
+
     protected override void do_logout() {
         debug("ACTION: logging out user.");
         get_session().deauthenticate();
@@ -244,6 +272,32 @@ public class Publisher : Publishing.RESTSupport.GooglePublisher {
         }
     }
 
+    private void do_upload() {
+        debug("ACTION: uploading media items to remote server.");
+
+        get_host().set_service_locked(true);
+
+        progress_reporter = get_host().serialize_publishables(
+            publishing_parameters.get_major_axis_size_pixels(),
+            publishing_parameters.get_strip_metadata());
+
+        // Serialization is a long and potentially cancellable operation, so before we use
+        // the publishables, make sure that the publishing interaction is still running. If it
+        // isn't the publishing environment may be partially torn down so do a short-circuit
+        // return
+        if (!is_running())
+            return;
+
+        Spit.Publishing.Publishable[] publishables = get_host().get_publishables();
+        Uploader uploader = new Uploader(get_session(), publishables, publishing_parameters);
+
+        uploader.upload_complete.connect(on_upload_complete);
+        uploader.upload_error.connect(on_upload_error);
+
+        uploader.upload(on_upload_status_updated);
+    }
+
+
     public override bool is_running() {
         return running;
     }
diff --git a/plugins/shotwell-publishing/PhotosPublishingPane.vala 
b/plugins/shotwell-publishing/PhotosPublishingPane.vala
index 45ca3f1f..d26fdfc5 100644
--- a/plugins/shotwell-publishing/PhotosPublishingPane.vala
+++ b/plugins/shotwell-publishing/PhotosPublishingPane.vala
@@ -17,6 +17,8 @@ internal class PublishingOptionsPane : Gtk.Box, Spit.Publishing.DialogPane {
     [GtkChild]
     private Gtk.Button logout_button;
     [GtkChild]
+    private Gtk.Button publish_button;
+    [GtkChild]
     private Gtk.ComboBoxText existing_albums_combo;
     [GtkChild]
     private Gtk.ComboBoxText size_combo;
@@ -24,6 +26,8 @@ internal class PublishingOptionsPane : Gtk.Box, Spit.Publishing.DialogPane {
     private Gtk.Label publish_to_label;
     [GtkChild]
     private Gtk.Label login_identity_label;
+    [GtkChild]
+    private Gtk.CheckButton strip_metadata_check;
 
     public signal void publish();
     public signal void logout();
@@ -41,6 +45,7 @@ internal class PublishingOptionsPane : Gtk.Box, Spit.Publishing.DialogPane {
         // populate any widgets whose contents are programmatically-generated.
         login_identity_label.set_label(_("You are logged into Google Photos as %s.").printf(
             parameters.get_user_name()));
+        strip_metadata_check.set_active(parameters.get_strip_metadata());
 
         if((parameters.get_media_type() & Spit.Publishing.Publisher.MediaType.PHOTO) == 0) {
             publish_to_label.set_label(_("Videos will appear in:"));
@@ -56,6 +61,9 @@ internal class PublishingOptionsPane : Gtk.Box, Spit.Publishing.DialogPane {
             size_combo.set_sensitive(true);
             size_combo.set_active(parameters.get_major_axis_size_selection_id());
         }
+
+        publish_button.clicked.connect (on_publish_clicked);
+        logout_button.clicked.connect (on_logout_clicked);
     }
 
     // DialogPane interface
@@ -68,9 +76,6 @@ internal class PublishingOptionsPane : Gtk.Box, Spit.Publishing.DialogPane {
     }
 
     public void on_pane_installed() {
-        if (90 < 0) {
-            print("%d", size_descriptions[0].major_axis_pixels);
-        }
         int default_album_id = -1;
         string last_album = parameters.get_target_album_name();
 
@@ -92,5 +97,27 @@ internal class PublishingOptionsPane : Gtk.Box, Spit.Publishing.DialogPane {
 
     public void on_pane_uninstalled() {
     }
+
+    private void on_publish_clicked() {
+        // size_combo won't have been set to anything useful if this is the first time we've
+        // published to Picasa, and/or we've only published video before, so it may be negative,
+        // indicating nothing was selected. Clamp it to a valid value...
+        int size_combo_last_active = (size_combo.get_active() >= 0) ? size_combo.get_active() : 0;
+
+        parameters.set_major_axis_size_selection_id(size_combo_last_active);
+        parameters.set_major_axis_size_pixels(
+            size_descriptions[size_combo_last_active].major_axis_pixels);
+        parameters.set_strip_metadata(strip_metadata_check.get_active());
+
+        Album[] albums = parameters.get_albums();
+
+        parameters.set_target_album_name(albums[existing_albums_combo.get_active()].name);
+        parameters.set_target_album_entry_id(albums[existing_albums_combo.get_active()].id);
+        publish();
+    }
+
+    private void on_logout_clicked() {
+        logout();
+    }
  }
 }
diff --git a/plugins/shotwell-publishing/PhotosService.vala b/plugins/shotwell-publishing/PhotosService.vala
index 35bb2432..0ffb4d4d 100644
--- a/plugins/shotwell-publishing/PhotosService.vala
+++ b/plugins/shotwell-publishing/PhotosService.vala
@@ -1,7 +1,7 @@
 namespace Publishing.GooglePhotos {
 
 public class Service : Object, Spit.Pluggable, Spit.Publishing.Service {
-    private const string ICON_FILENAME = "gnome-photos.svg";
+    private const string ICON_FILENAME = "google-photos.svg";
 
     private static Gdk.Pixbuf[] icon_pixbuf_set = null;
 
@@ -17,7 +17,7 @@ public class Service : Object, Spit.Pluggable, Spit.Publishing.Service {
     }
 
     public unowned string get_id() {
-        return "org.gnome.shotwell.publishing.photos";
+        return "org.gnome.shotwell.publishing.google-photos";
     }
 
     public unowned string get_pluggable_name() {
diff --git a/plugins/shotwell-publishing/PhotosUploader.vala b/plugins/shotwell-publishing/PhotosUploader.vala
new file mode 100644
index 00000000..dd52a1a7
--- /dev/null
+++ b/plugins/shotwell-publishing/PhotosUploader.vala
@@ -0,0 +1,74 @@
+namespace Publishing.GooglePhotos {
+
+internal class UploadTransaction :
+    Publishing.RESTSupport.GooglePublisher.AuthenticatedTransaction {
+    private PublishingParameters parameters;
+    private Publishing.RESTSupport.GoogleSession session;
+    private Spit.Publishing.Publishable publishable;
+    private MappedFile mapped_file;
+
+    public UploadTransaction(Publishing.RESTSupport.GoogleSession session,
+        PublishingParameters parameters, Spit.Publishing.Publishable publishable) {
+        base(session, "https://photoslibrary.googleapis.com/v1/uploads";,
+            Publishing.RESTSupport.HttpMethod.POST);
+        assert(session.is_authenticated());
+        this.session = session;
+        this.parameters = parameters;
+        this.publishable = publishable;
+    }
+
+    public override void execute() throws Spit.Publishing.PublishingError {
+        var basename = publishable.get_param_string(Spit.Publishing.Publishable.PARAM_STRING_BASENAME);
+
+        // attempt to map the binary image data from disk into memory 
+        try {
+            mapped_file = new MappedFile(publishable.get_serialized_file().get_path(), false);
+        } catch (FileError e) {
+            string msg = "Picasa: couldn't read data from %s: %s".printf(
+                publishable.get_serialized_file().get_path(), e.message);
+            warning("%s", msg);
+            
+            throw new Spit.Publishing.PublishingError.LOCAL_FILE_ERROR(msg);
+        }
+        unowned uint8[] photo_data = (uint8[]) mapped_file.get_contents();
+        photo_data.length = (int) mapped_file.get_length();
+
+        // bind the binary image data read from disk into a Soup.Buffer object so that we
+        // can attach it to the multipart request, then actaully append the buffer
+        // to the multipart request. Then, set the MIME type for this part.
+        Soup.Buffer bindable_data = new Soup.Buffer(Soup.MemoryUse.TEMPORARY, photo_data);
+
+        // create a message that can be sent over the wire whose payload is the multipart container
+        // that we've been building up
+        var outbound_message = new Soup.Message ("POST", get_endpoint_url());
+        outbound_message.request_headers.append("Authorization", "Bearer " +
+            session.get_access_token());
+        outbound_message.request_headers.append("X-Goog-Upload-File-Name", basename);
+        outbound_message.request_headers.append("X-Goog-Upload-Protocol", "raw");
+        outbound_message.request_headers.set_content_type("application/octet-stream", null);
+        outbound_message.request_body.append_buffer (bindable_data);
+        set_message(outbound_message);
+
+        // send the message and get its response
+        set_is_executed(true);
+        send();
+    }
+}
+
+internal class Uploader : Publishing.RESTSupport.BatchUploader {
+    private PublishingParameters parameters;
+
+    public Uploader(Publishing.RESTSupport.GoogleSession session,
+        Spit.Publishing.Publishable[] publishables, PublishingParameters parameters) {
+        base(session, publishables);
+        
+        this.parameters = parameters;
+    }
+    
+    protected override Publishing.RESTSupport.Transaction create_transaction(
+        Spit.Publishing.Publishable publishable) {
+        return new UploadTransaction((Publishing.RESTSupport.GoogleSession) get_session(),
+            parameters, get_current_publishable());
+    }
+}
+}
diff --git a/plugins/shotwell-publishing/meson.build b/plugins/shotwell-publishing/meson.build
index cfee47cc..fd27a99e 100644
--- a/plugins/shotwell-publishing/meson.build
+++ b/plugins/shotwell-publishing/meson.build
@@ -8,7 +8,8 @@ shotwell_publishing_sources = [
     'PiwigoPublishing.vala',
     'PhotosPublisher.vala',
     'PhotosService.vala',
-    'PhotosPublishingPane.vala'
+    'PhotosPublishingPane.vala',
+    'PhotosUploader.vala'
 ]
 
 shotwell_publishing_resources = gnome.compile_resources('publishing-resource',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]