aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Makefile.am8
-rw-r--r--src/fetch-file.vala86
-rw-r--r--src/metadata-menu-item.vala128
-rw-r--r--src/metadata-widget.c38
-rw-r--r--src/player-item.vala26
-rw-r--r--src/sound-service.c7
-rw-r--r--vapi/common-defs.vapi2
7 files changed, 262 insertions, 33 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 2a4e937..e85ed93 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -67,7 +67,9 @@ music_bridge_VALASOURCES = \
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 @@ music_bridge_VALAFLAGS = \
--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)
diff --git a/src/fetch-file.vala b/src/fetch-file.vala
new file mode 100644
index 0000000..1811cc1
--- /dev/null
+++ b/src/fetch-file.vala
@@ -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@canonical.com>
+ * Conor Curran <conor.curran@canonical.com>
+ */
+
+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);
+ }
+}
diff --git a/src/metadata-menu-item.vala b/src/metadata-menu-item.vala
index 0bb4a85..3f71653 100644
--- a/src/metadata-menu-item.vala
+++ b/src/metadata-menu-item.vala
@@ -17,18 +17,141 @@ You should have received a copy of the GNU General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-using Dbusmenu;
using Gee;
using DbusmenuMetadata;
+using Gdk;
public class MetadataMenuitem : PlayerItem
{
+ public const string ALBUM_ART_DIR_SUFFIX = "indicators/sound/album-art-cache";
+
+ public static string album_art_cache_dir;
+ private static FetchFile fetcher;
+ private string previous_temp_album_art_path;
+
public MetadataMenuitem()
{
Object(item_type: MENUITEM_TYPE);
reset(attributes_format());
}
+
+ construct{
+ MetadataMenuitem.clean_album_art_temp_dir();
+ this.previous_temp_album_art_path = null;
+ this.album_art_cache_dir = MetadataMenuitem.create_album_art_temp_dir();
+ }
+ private static void clean_album_art_temp_dir()
+ {
+ string path = GLib.Path.build_filename(Environment.get_user_cache_dir(), ALBUM_ART_DIR_SUFFIX);
+
+ GLib.File? album_art_dir = GLib.File.new_for_path(path);
+
+ if(delete_album_art_contents(album_art_dir) == false)
+ {
+ warning("could not remove the temp album art files %s", path);
+ }
+ }
+
+ private static string? create_album_art_temp_dir()
+ {
+ string path = GLib.Path.build_filename(Environment.get_user_cache_dir(), ALBUM_ART_DIR_SUFFIX);
+ if(DirUtils.create(path, 0700) == -1){
+ warning("could not create a temp dir for remote album art, it must have been created already");
+ }
+ return path;
+ }
+
+ private static bool delete_album_art_contents (GLib.File dir)
+ {
+ bool result = true;
+ try {
+ var e = dir.enumerate_children (FILE_ATTRIBUTE_STANDARD_NAME,
+ FileQueryInfoFlags.NOFOLLOW_SYMLINKS,
+ null);
+ while (true)
+ {
+ var file = e.next_file (null);
+
+ debug("file name = %s", file.get_name());
+
+ if (file == null)
+ break;
+
+ var child = dir.get_child (file.get_name ());
+
+ try {
+ child.delete (null);
+ } catch (Error error_) {
+ warning (@"Unable to delete file '$(child.get_basename ()): $(error_.message)");
+ result = false;
+ }
+ }
+ } catch (Error error) {
+ warning (@"Unable to read files from directory '$(dir.get_basename ())': %s",
+ error.message);
+ result = false;
+ }
+ return result;
+ }
+
+ public void fetch_art(string uri, string prop)
+ {
+ File art_file = File.new_for_uri(uri);
+ if(art_file.is_native() == true){
+ string path;
+ try{
+ path = Filename.from_uri(uri.strip());
+ this.property_set(prop, path);
+ }
+ catch(ConvertError e){
+ warning("Problem converting URI %s to file path",
+ uri);
+ }
+ // eitherway return, the artwork was local
+ return;
+ }
+ debug("fetch_art -remotely %s", this.album_art_cache_dir);
+ // If we didn't manage to create the temp dir
+ // don't bother with remote
+ if(this.album_art_cache_dir == null){
+ return;
+ }
+ // green light to go remote
+ 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 ();
+ string path = this.album_art_cache_dir.concat("/downloaded-coverart-XXXXXX");
+ int r = FileUtils.mkstemp(path);
+ if(r != -1){
+ icon.save (path, loader.get_format().get_name());
+ this.property_set(property, path);
+ if(this.previous_temp_album_art_path != null){
+ FileUtils.remove(this.previous_temp_album_art_path);
+ }
+ this.previous_temp_album_art_path = path;
+ }
+ }
+ catch(GLib.Error e){
+ warning("Problem creating file from bytearray fetched from the interweb - error: %s",
+ e.message);
+ }
+ }
public static HashSet<string> attributes_format()
{
@@ -38,6 +161,5 @@ public class MetadataMenuitem : PlayerItem
attrs.add(MENUITEM_ALBUM);
attrs.add(MENUITEM_ARTURL);
return attrs;
- }
-
+ }
}
diff --git a/src/metadata-widget.c b/src/metadata-widget.c
index 740ad6d..18ebd38 100644
--- a/src/metadata-widget.c
+++ b/src/metadata-widget.c
@@ -25,6 +25,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#include "metadata-widget.h"
#include "common-defs.h"
#include <gtk/gtk.h>
+#include <glib.h>
static DbusmenuMenuitem* twin_item;
@@ -69,7 +70,7 @@ static void image_set_from_pixbuf (GtkWidget *widget,
MetadataWidget* metadata,
GdkPixbuf *source);
-
+static void draw_album_art_placeholder(GtkWidget *metadata);
G_DEFINE_TYPE (MetadataWidget, metadata_widget, GTK_TYPE_MENU_ITEM);
@@ -178,7 +179,7 @@ metadata_widget_finalize (GObject *object)
/**
* 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 +187,33 @@ metadata_image_expose (GtkWidget *metadata, GdkEventExpose *event, gpointer user
g_return_val_if_fail(IS_METADATA_WIDGET(user_data), FALSE);
MetadataWidget* widget = METADATA_WIDGET(user_data);
MetadataWidgetPrivate * priv = METADATA_WIDGET_GET_PRIVATE(widget);
-
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){
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 +265,7 @@ metadata_image_expose (GtkWidget *metadata, GdkEventExpose *event, gpointer user
g_object_unref(pcontext);
g_string_free (string, TRUE);
cairo_destroy (cr);
-
- return TRUE;
+
}
/* Suppress/consume keyevents */
@@ -314,7 +323,12 @@ metadata_widget_property_update(DbusmenuMenuitem* item, gchar* property,
}
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));
+ g_string_overwrite(priv->image_path, 0, g_value_get_string (value));
+ // if its a remote image queue a redraw incase the download took too long
+ if (g_str_has_prefix(g_value_get_string (value), g_get_user_cache_dir())){
+ g_debug("the image update is a download so redraw");
+ gtk_widget_queue_draw(GTK_WIDGET(mitem));
+ }
}
}
diff --git a/src/player-item.vala b/src/player-item.vala
index fbfacbd..68ae6ef 100644
--- a/src/player-item.vala
+++ b/src/player-item.vala
@@ -25,7 +25,7 @@ public class PlayerItem : Dbusmenu.Menuitem
public PlayerController owner {get; construct;}
public string item_type { get; construct; }
private const int EMPTY = -1;
-
+
public PlayerItem(string type)
{
Object(item_type: type);
@@ -42,6 +42,12 @@ public class PlayerItem : Dbusmenu.Menuitem
}
}
+ /**
+ * update()
+ * Base update method for playeritems, takes the attributes and the incoming updates
+ * and attmepts to update the appropriate props on the object.
+ * Album art is handled separately to deal with remote and local file paths.
+ */
public void update(HashTable<string, Value?> data, HashSet<string> attributes)
{
debug("PlayerItem::update()");
@@ -59,16 +65,14 @@ public class PlayerItem : Dbusmenu.Menuitem
if (v.holds (typeof (string))){
string update = v.get_string().strip();
debug("with value : %s", update);
- // Special case for the arturl URI's.
- if(property.contains("mpris:artUrl")){
- try{
- update = Filename.from_uri(update.strip());
- }
- catch(ConvertError e){
- warning("Problem converting URI %s to file path", update);
- }
+ if(property.contains("mpris:artUrl")){
+ // We know its a metadata instance because thats the only
+ // object with the arturl prop
+ MetadataMenuitem metadata = this as MetadataMenuitem;
+ metadata.fetch_art(update.strip(), property);
+ continue;
}
- this.property_set(property, update);
+ this.property_set(property, update);
}
else if (v.holds (typeof (int))){
debug("with value : %i", v.get_int());
@@ -83,7 +87,6 @@ public class PlayerItem : Dbusmenu.Menuitem
this.property_set_bool(property, v.get_boolean());
}
}
-
if(this.property_get_bool(MENUITEM_PROP_VISIBLE) == false){
this.property_set_bool(MENUITEM_PROP_VISIBLE, true);
}
@@ -101,5 +104,6 @@ public class PlayerItem : Dbusmenu.Menuitem
}
return false;
}
+
}
diff --git a/src/sound-service.c b/src/sound-service.c
index 12f067e..51f5f37 100644
--- a/src/sound-service.c
+++ b/src/sound-service.c
@@ -40,14 +40,13 @@ service_shutdown (IndicatorService *service, gpointer user_data)
{
if (mainloop != NULL) {
g_debug("Service shutdown !");
- // TODO: uncomment for release !!
- close_pulse_activites();
- g_main_loop_quit(mainloop);
+ //TODO: uncomment for release !!
+ //close_pulse_activites();
+ //g_main_loop_quit(mainloop);
}
return;
}
-
/**
main:
**/
diff --git a/vapi/common-defs.vapi b/vapi/common-defs.vapi
index c083e2a..84fb924 100644
--- a/vapi/common-defs.vapi
+++ b/vapi/common-defs.vapi
@@ -1,6 +1,6 @@
/*
Copyright 2010 Canonical Ltd.
-
+
Authors:
Conor Curran <conor.curran@canonical.com>