← Back to team overview

ayatana-commits team mailing list archive

[Merge] lp:~cjcurran/indicator-sound/remote-art-handling into lp:indicator-sound

 

Conor Curran has proposed merging lp:~cjcurran/indicator-sound/remote-art-handling into lp:indicator-sound.

Requested reviews:
  Indicator Applet Developers (indicator-applet-developers)
Related bugs:
  #627505 Crashes if Rhythmbox is playing from Last.FM
  https://bugs.launchpad.net/bugs/627505


- Deals with last fm plugin for rhythmbox which uses remote artwork. Dynamically remote artwork will now be fetched asynchronously, rounded and exposed. 
-- 
https://code.launchpad.net/~cjcurran/indicator-sound/remote-art-handling/+merge/34396
Your team ayatana-commits is subscribed to branch lp:indicator-sound.
=== modified file 'src/Makefile.am'
--- src/Makefile.am	2010-08-27 11:25:47 +0000
+++ src/Makefile.am	2010-09-02 10:31:05 +0000
@@ -67,7 +67,9 @@
 	player-controller.vala \
 	mpris2-controller.vala \
 	player-item.vala \
-	familiar-players-db.vala
+	familiar-players-db.vala \
+	fetch-file.vala
+
 
 music_bridge_VALAFLAGS = \
   --ccode \
@@ -81,7 +83,9 @@
   --pkg Dbusmenu-Glib-0.2 \
   --pkg common-defs \
 	--pkg dbus-glib-1 \
-	--pkg gio-unix-2.0
+	--pkg gio-unix-2.0 \
+	--pkg gdk-pixbuf-2.0 
+
 	 
  $(MAINTAINER_VALAFLAGS)
 

=== modified file 'src/common-defs.h'
--- src/common-defs.h	2010-08-24 15:22:35 +0000
+++ src/common-defs.h	2010-09-02 10:31:05 +0000
@@ -25,6 +25,7 @@
 #define SIGNAL_SINK_AVAILABLE_UPDATE 						"SinkAvailableUpdate"
 
 #define DBUSMENU_PROPERTY_EMPTY									-1
+#define DBUSMENU_PLAYER_ITEM_REMOTE_FILEPATH			"/tmp/indicator-sound-downloaded-album-art"
 
 /* DBUS Custom Items */
 #define DBUSMENU_VOLUME_MENUITEM_TYPE          	"x-canonical-ido-volume-type"

=== added file 'src/fetch-file.vala'
--- src/fetch-file.vala	1970-01-01 00:00:00 +0000
+++ src/fetch-file.vala	2010-09-02 10:31:05 +0000
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2010 Canonical, Ltd.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License
+ * version 3.0 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License version 3.0 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Authors
+ * 			Gordon Allott <gord.allott@xxxxxxxxxxxxx>
+ *			Conor Curran <conor.curran@xxxxxxxxxxxxx>
+ */
+
+public class FetchFile : Object
+{
+  /* public variables */
+  public string uri {get; construct;}
+	public string intended_property {get; construct;}
+
+  /* private variables */
+  private DataInputStream stream;
+  private File? file;
+  private ByteArray data;
+
+  /* public signals */
+  public signal void failed ();
+  public signal void completed (ByteArray data, string property);
+
+  public FetchFile (string uri, string prop)
+  {
+    Object (uri: uri, intended_property: prop);
+  }
+
+  construct
+  {
+    this.file = File.new_for_uri(this.uri);
+    this.data = new ByteArray ();
+  }
+
+  public async void fetch_data ()
+  {
+    try {
+      this.stream = new DataInputStream(this.file.read(null));
+      this.stream.set_byte_order (DataStreamByteOrder.LITTLE_ENDIAN);
+    } catch (GLib.Error e) {
+      this.failed ();
+    }
+    this.read_something_async ();
+  }
+
+  private async void read_something_async ()
+  {
+    ssize_t size = 1024;
+    uint8[] buffer = new uint8[size];
+
+    ssize_t bufsize = 1;
+    do {
+      try {
+        bufsize = yield this.stream.read_async (buffer, size, GLib.Priority.DEFAULT, null);
+        if (bufsize < 1) { break;}
+
+        if (bufsize != size)
+          {
+            uint8[] cpybuf = new uint8[bufsize];
+            Memory.copy (cpybuf, buffer, bufsize);
+            this.data.append (cpybuf);
+          }
+        else
+          {
+            this.data.append (buffer);
+          }
+      } catch (Error e) {
+        this.failed ();
+      }
+    } while (bufsize > 0);
+    this.completed (this.data, this.intended_property);
+  }
+}

=== modified file 'src/metadata-menu-item.vala'
--- src/metadata-menu-item.vala	2010-08-24 16:59:15 +0000
+++ src/metadata-menu-item.vala	2010-09-02 10:31:05 +0000
@@ -17,7 +17,6 @@
 with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-using Dbusmenu;
 using Gee;
 using DbusmenuMetadata;
 

=== modified file 'src/metadata-widget.c'
--- src/metadata-widget.c	2010-08-25 19:29:39 +0000
+++ src/metadata-widget.c	2010-09-02 10:31:05 +0000
@@ -36,6 +36,7 @@
 	GtkWidget* album_art;
   GString*	 image_path;
   GString*	 old_image_path;
+	GString* 	 remote_image_path;
 	GtkWidget* artist_label;
 	GtkWidget* piece_label;
 	GtkWidget* container_label;	
@@ -69,7 +70,7 @@
 												           MetadataWidget* metadata,
 												           GdkPixbuf *source);
 
-
+static void draw_album_art_placeholder(GtkWidget *metadata);
 
 G_DEFINE_TYPE (MetadataWidget, metadata_widget, GTK_TYPE_MENU_ITEM);
 
@@ -104,6 +105,7 @@
 	priv->album_art = gtk_image_new();
 	priv->image_path = g_string_new(dbusmenu_menuitem_property_get(twin_item, DBUSMENU_METADATA_MENUITEM_ARTURL));
 	priv->old_image_path = g_string_new("");
+	priv->remote_image_path = g_string_new(DBUSMENU_PLAYER_ITEM_REMOTE_FILEPATH);
 	g_debug("Metadata::At startup and image path = %s", priv->image_path->str);
 	
   g_signal_connect(priv->album_art, "expose-event", 
@@ -178,7 +180,7 @@
 
 /**
  * We override the expose method to enable primitive drawing of the 
- * empty album art image (and soon rounded rectangles on the album art)
+ * empty album art image and rounded rectangles on the album art.
  */
 static gboolean
 metadata_image_expose (GtkWidget *metadata, GdkEventExpose *event, gpointer user_data)
@@ -186,24 +188,37 @@
 	g_return_val_if_fail(IS_METADATA_WIDGET(user_data), FALSE);
 	MetadataWidget* widget = METADATA_WIDGET(user_data);
 	MetadataWidgetPrivate * priv = METADATA_WIDGET_GET_PRIVATE(widget);	
-	
+	g_debug("expose");
 	if(priv->image_path->len > 0){
-		
-	  if(g_string_equal(priv->image_path, priv->old_image_path) == FALSE){
+	  if(g_string_equal(priv->image_path, priv->old_image_path) == FALSE || 
+	     (g_string_equal(priv->image_path, priv->remote_image_path) == TRUE &&
+	      g_string_equal(priv->old_image_path, priv->remote_image_path) == FALSE)){					
+			g_debug("and we are in");
 			GdkPixbuf* pixbuf;
 			pixbuf = gdk_pixbuf_new_from_file(priv->image_path->str, NULL);
-			g_debug("metadata_widget_expose, album art update -> pixbuf from %s",
-						  priv->image_path->str); 
+			g_debug("metadata_load_new_image -> pixbuf from %s",
+							priv->image_path->str); 
+			if(GDK_IS_PIXBUF(pixbuf) == FALSE){
+				g_debug("problem loading the downloaded image just use the placeholder instead");
+				draw_album_art_placeholder(metadata);
+				return TRUE;				
+			}
 			pixbuf = gdk_pixbuf_scale_simple(pixbuf,60, 60, GDK_INTERP_BILINEAR);
 			image_set_from_pixbuf (metadata, widget, pixbuf);
 			g_string_erase(priv->old_image_path, 0, -1);
 			g_string_overwrite(priv->old_image_path, 0, priv->image_path->str); 
 
-			g_object_unref(pixbuf);			
+			g_object_unref(pixbuf);				
 		}
 		return FALSE;				
 	}
-	
+	draw_album_art_placeholder(metadata);
+	return TRUE;
+}
+
+static void draw_album_art_placeholder(GtkWidget *metadata)
+{
+		
 	cairo_t *cr;	
 	cr = gdk_cairo_create (metadata->window);
 	GtkAllocation alloc;
@@ -255,8 +270,7 @@
 	g_object_unref(pcontext);
 	g_string_free (string, TRUE);
 	cairo_destroy (cr);	
-	
-	return TRUE;
+
 }
 
 /* Suppress/consume keyevents */
@@ -315,6 +329,12 @@
 	else if(g_ascii_strcasecmp(DBUSMENU_METADATA_MENUITEM_ARTURL, property) == 0){
 		g_string_erase(priv->image_path, 0, -1);
 		g_string_overwrite(priv->image_path, 0, g_value_get_string (value)); 
+		// Basically force expose the reload the image because we have an image update
+		// but we are using remote images i.e. the same file path but different images
+		if(g_string_equal(priv->image_path, priv->remote_image_path) == TRUE){
+			g_string_erase(priv->old_image_path, 0, -1);
+			gtk_widget_queue_draw(GTK_WIDGET(mitem));
+		}
 	}		
 }
 

=== modified file 'src/player-item.vala'
--- src/player-item.vala	2010-08-24 16:59:15 +0000
+++ src/player-item.vala	2010-09-02 10:31:05 +0000
@@ -19,13 +19,16 @@
 
 using Dbusmenu;
 using Gee;
+using Gdk;
+using DbusmenuPlayer;
 
 public class PlayerItem : Dbusmenu.Menuitem
 {
 	public PlayerController owner {get; construct;}
 	public string item_type { get; construct; }
 	private const int EMPTY = -1;
-	
+	private FetchFile fetcher;
+
 	public PlayerItem(string type)
 	{		
 		Object(item_type: type);
@@ -61,14 +64,23 @@
 				debug("with value : %s", update);
 				// Special case for the arturl URI's.
 				if(property.contains("mpris:artUrl")){
-					try{
-						update = Filename.from_uri(update.strip());
+					if(update.has_prefix("http://";)){
+						// This is asyncronous so handle it offline
+					  this.fetch_remote_art(update.strip(), property);
+					 	continue;					                     
 					}
-					catch(ConvertError e){
-						warning("Problem converting URI %s to file path", update); 
+					else{
+						// The file is local, just parse the string
+						try{
+							update = Filename.from_uri(update.strip());			
+						}
+						catch(ConvertError e){
+							warning("Problem converting URI %s to file path",
+							        update); 
+						}						
 					}
 				}
-				this.property_set(property, update);
+				this.property_set(property, update);							
 			}			    
 			else if (v.holds (typeof (int))){
 				debug("with value : %i", v.get_int());
@@ -101,5 +113,34 @@
 		}
 		return false;
 	}
+
+	public void fetch_remote_art(string uri, string prop)
+	{
+		this.fetcher = new FetchFile (uri, prop);
+		this.fetcher.failed.connect (() => { this.on_fetcher_failed ();});
+		this.fetcher.completed.connect (this.on_fetcher_completed);
+		this.fetcher.fetch_data ();		
+	}
+
+	private void on_fetcher_failed ()
+	{
+		warning("on_fetcher_failed -> could not fetch artwork");	
+	}
+
+	private void on_fetcher_completed(ByteArray update, string property)
+	{
+		try{
+			PixbufLoader loader = new PixbufLoader ();
+			loader.write (update.data, update.len);
+			loader.close ();
+			Pixbuf icon = loader.get_pixbuf ();				
+			icon.save (ITEM_REMOTE_FILEPATH, loader.get_format().get_name());		
+			this.property_set(property, ITEM_REMOTE_FILEPATH);
+		}
+	  catch(GLib.Error e){
+			warning("Problem fetching file from the interweb - error: %s",
+			        e.message);
+		}				
+	}		
 }
 

=== modified file 'src/sound-service.c'
--- src/sound-service.c	2010-08-25 19:33:25 +0000
+++ src/sound-service.c	2010-09-02 10:31:05 +0000
@@ -40,14 +40,13 @@
 {
   if (mainloop != NULL) {
     g_debug("Service shutdown !");
-    // TODO: uncomment for release !!
+    //TODO: uncomment for release !!
     close_pulse_activites();
     g_main_loop_quit(mainloop);
   }
   return;
 }
 
-
 /**
 main:
 **/

=== modified file 'vapi/common-defs.vapi'
--- vapi/common-defs.vapi	2010-07-21 09:56:15 +0000
+++ vapi/common-defs.vapi	2010-09-02 10:31:05 +0000
@@ -1,6 +1,6 @@
 /*
 Copyright 2010 Canonical Ltd.
-
+ 
 Authors:
     Conor Curran <conor.curran@xxxxxxxxxxxxx>
 
@@ -27,6 +27,11 @@
 }
 
 [CCode (cheader_filename = "common-defs.h")]
+namespace DbusmenuPlayer{
+	public const string ITEM_REMOTE_FILEPATH;	
+}
+
+[CCode (cheader_filename = "common-defs.h")]
 namespace DbusmenuTransport{
 	public const string MENUITEM_TYPE;
 	public const string MENUITEM_PLAY_STATE;


Follow ups