Re: Vimeo plugin



Hi,

Here goes my patch with the changes pointed by Victor.

Removed redundant commands from configure.ac, deleted trailing whitespaces and removed unused variable declaration.


Cheers,

--
Joaquim Rocha
http://www.joaquimrocha.com

On Fri, Apr 23, 2010 at 3:46 PM, Joaquim Rocha <joaquimrocha1 gmail com> wrote:
Hi, I agree with Juan and that initialization is, in fact, needed.
Libsoup async session is the one that requires it.

I'll remove the unnecessary stuff Victor pointed out.


Cheers,

--
Joaquim Rocha
http://www.joaquimrocha.com



On Fri, Apr 23, 2010 at 2:27 PM, Juan A. Suarez Romero <jasuarez igalia com> wrote:
On Fri, 2010-04-23 at 13:41 +0200, Víctor M. Jáquez L. wrote:
> > +  if (!g_thread_supported ()) {
> > +    g_thread_init (NULL);
> > +  }
>
> I'm still not convinced if this initialization is really needed here.
> IMHO
> this is always carried out by the client application.

Client shouldn't take care about how grilo's plugins are coded. So I'm
not sure if this initialization should be done in client's side.

Moreover, let's figure out that none of our plugins require gthread
support, so clients wouldn't invoke g_thread_init.

What would happen if a third-party plugin that requires gthread is
installed in the system?. It is supposedly that client should be able to
use it without rebuilding the client.


IMHO, in Grilo, every plugin should initialize what they need to use.

       J.A.


_______________________________________________
grilo-list mailing list
grilo-list gnome org
http://mail.gnome.org/mailman/listinfo/grilo-list






From efd4539b0834547169afd03c91467082a1eabf8f Mon Sep 17 00:00:00 2001
From: Joaquim Rocha <jrocha igalia com>
Date: Wed, 21 Apr 2010 10:33:00 +0200
Subject: [PATCH] Added Vimeo plugin

The Vimeo plugin allows to search videos, retrieving, among other data, the video's play URL.
---
 configure.ac          |   47 +++++
 src/Makefile.am       |    6 +-
 src/vimeo/Makefile.am |   34 ++++
 src/vimeo/grl-vimeo.c |  386 ++++++++++++++++++++++++++++++++++++
 src/vimeo/grl-vimeo.h |   77 ++++++++
 src/vimeo/gvimeo.c    |  518 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/vimeo/gvimeo.h    |  112 +++++++++++
 7 files changed, 1179 insertions(+), 1 deletions(-)
 create mode 100644 src/vimeo/Makefile.am
 create mode 100644 src/vimeo/grl-vimeo.c
 create mode 100644 src/vimeo/grl-vimeo.h
 create mode 100644 src/vimeo/gvimeo.c
 create mode 100644 src/vimeo/gvimeo.h

diff --git a/configure.ac b/configure.ac
index 5a0e878..5add819 100644
--- a/configure.ac
+++ b/configure.ac
@@ -107,6 +107,12 @@ AC_SUBST(GDATA_LIBS)
 AC_SUBST(HAVE_GDATA)
 AM_CONDITIONAL(HAVE_GDATA, test "x$HAVE_GDATA" = "xyes")
 
+PKG_CHECK_MODULES(LIBSOUP, libsoup-2.4, HAVE_LIBSOUP=yes, HAVE_LIBSOUP=no)
+
+AC_CHECK_HEADER([gcrypt.h], HAVE_GCRYPT=yes, HAVE_GCRYPT=no)
+
+PKG_CHECK_MODULES(GTHREAD, gthread-2.0, HAVE_GTHREAD=yes, HAVE_GTHREAD=no)
+
 # ----------------------------------------------------------
 # GUPNP-AV VERSION
 # ----------------------------------------------------------
@@ -548,6 +554,46 @@ then
 fi
 
 # ----------------------------------------------------------
+# BUILD VIMEO PLUGIN
+# ----------------------------------------------------------
+
+AC_ARG_ENABLE(vimeo,
+        AC_HELP_STRING([--enable-vimeo],
+                [enable Vimeo plugin (default: auto)]),
+        [
+                case "$enableval" in
+                     yes)
+                        if test "x$HAVE_XML" = "xno"; then
+                           AC_MSG_ERROR([xml2 not found, install it or use --disable-vimeo])
+                        fi
+                        if test "x$HAVE_LIBSOUP" = "xno"; then
+                           AC_MSG_ERROR([libsoup not found, install it or use --disable-vimeo])
+                        fi
+			if test "x$HAVE_GCRYPT" = "xno"; then
+                           AC_MSG_ERROR([libgcrypt not found, install it or use --disable-vimeo])
+                        fi
+			if test "x$HAVE_GTHREAD" = "xno"; then
+                           AC_MSG_ERROR([gthread not found, install it or use --disable-vimeo])
+                        fi
+                        ;;
+                esac
+        ],
+        [
+                if test "x$HAVE_XML" = "xyes" -a "x$HAVE_LIBSOUP" = "xyes" -a "x$HAVE_GCRYPT" = "xyes" -a "x$HAVE_GTHREAD" = "xyes"; then
+                   enable_vimeo=yes
+                else
+                   enable_vimeo=no
+                fi
+        ])
+
+AM_CONDITIONAL([VIMEO_PLUGIN], [test "x$enable_vimeo" = "xyes"])
+GRL_PLUGINS_ALL="$GRL_PLUGINS_ALL vimeo"
+if test "x$enable_vimeo" = "xyes"
+then
+	GRL_PLUGINS_ENABLED="$GRL_PLUGINS_ENABLED vimeo"
+fi
+
+# ----------------------------------------------------------
 # GETTEXT
 # ----------------------------------------------------------
 
@@ -583,6 +629,7 @@ AC_CONFIG_FILES([
   src/shoutcast/Makefile
   src/apple-trailers/Makefile
   src/metadata-store/Makefile
+  src/vimeo/Makefile
   test/Makefile
 ])
 
diff --git a/src/Makefile.am b/src/Makefile.am
index c45f9d3..e00b176 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -55,7 +55,11 @@ if METADATA_STORE_PLUGIN
 SUBDIRS += metadata-store
 endif
 
-DIST_SUBDIRS = youtube fake-metadata filesystem jamendo lastfm-albumart upnp flickr podcasts bookmarks shoutcast apple-trailers metadata-store
+if VIMEO_PLUGIN
+SUBDIRS += vimeo
+endif
+
+DIST_SUBDIRS = youtube fake-metadata filesystem jamendo lastfm-albumart upnp flickr podcasts bookmarks shoutcast apple-trailers metadata-store vimeo
 
 MAINTAINERCLEANFILES = \
         *.in \
diff --git a/src/vimeo/Makefile.am b/src/vimeo/Makefile.am
new file mode 100644
index 0000000..4526a2c
--- /dev/null
+++ b/src/vimeo/Makefile.am
@@ -0,0 +1,34 @@
+#
+# Makefile.am
+#
+# Author: Joaquim Rocha <jrocha igalia com>
+#
+# Copyright (C) 2010 Igalia S.L. All rights reserved.
+
+lib_LTLIBRARIES		 = libgrlvimeo.la
+
+libgrlvimeo_la_CFLAGS =	\
+	$(DEPS_CFLAGS)		\
+	$(XML_CFLAGS)		\
+	$(GTHREAD_CFLAGS)	\
+	$(LIBSOUP_CFLAGS)
+
+libgrlvimeo_la_LIBADD =	\
+	$(DEPS_LIBS)		\
+	$(XML_LIBS)		\
+	$(GTHREAD_LIBS)		\
+	$(LIBSOUP_LIBS)
+
+libgrlvimeo_la_SOURCES =	\
+	grl-vimeo.c		\
+	grl-vimeo.h		\
+	gvimeo.c		\
+	gvimeo.h
+
+libdir=$(GRL_PLUGINS_DIR)
+
+MAINTAINERCLEANFILES =	\
+	*.in		\
+	*~
+
+DISTCLEANFILES = $(MAINTAINERCLEANFILES)
diff --git a/src/vimeo/grl-vimeo.c b/src/vimeo/grl-vimeo.c
new file mode 100644
index 0000000..9823b33
--- /dev/null
+++ b/src/vimeo/grl-vimeo.c
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2010 Igalia S.L.
+ *
+ * Contact: Iago Toral Quiroga <itoral igalia com>
+ *
+ * Authors: Joaquim Rocha <jrocha igalia com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <grilo.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "grl-vimeo.h"
+#include "gvimeo.h"
+
+#define GRL_VIMEO_SOURCE_GET_PRIVATE(object)                           \
+  (G_TYPE_INSTANCE_GET_PRIVATE((object),                                \
+                               GRL_VIMEO_SOURCE_TYPE,                  \
+                               GrlVimeoSourcePrivate))
+
+/* --------- Logging  -------- */
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "grl-vimeo"
+
+/* --- Plugin information --- */
+
+#define PLUGIN_ID   "grl-vimeo"
+#define PLUGIN_NAME "Vimeo"
+#define PLUGIN_DESC "A plugin for browsing and searching Vimeo videos"
+
+#define SOURCE_ID   "grl-vimeo"
+#define SOURCE_NAME "Vimeo"
+#define SOURCE_DESC "A source for browsing and searching Vimeo videos"
+
+#define AUTHOR      "Igalia S.L."
+#define LICENSE     "LGPL"
+#define SITE        "http://www.igalia.com";
+
+typedef struct {
+  GrlMediaSourceSearchSpec *ss;
+  gint offset;
+  gint page;
+} SearchData;
+
+struct _GrlVimeoSourcePrivate {
+  GVimeo *vimeo;
+};
+
+static GrlVimeoSource *grl_vimeo_source_new (void);
+
+gboolean grl_vimeo_plugin_init (GrlPluginRegistry *registry,
+				const GrlPluginInfo *plugin,
+				GList *configs);
+
+static const GList *grl_vimeo_source_supported_keys (GrlMetadataSource *source);
+
+static void grl_vimeo_source_metadata (GrlMediaSource *source,
+				       GrlMediaSourceMetadataSpec *ss);
+
+static void grl_vimeo_source_search (GrlMediaSource *source,
+				     GrlMediaSourceSearchSpec *ss);
+
+/* =================== Vimeo Plugin  =============== */
+
+gboolean
+grl_vimeo_plugin_init (GrlPluginRegistry *registry,
+                        const GrlPluginInfo *plugin,
+                        GList *configs)
+{
+  const gchar *vimeo_key;
+  const gchar *vimeo_secret;
+  const GrlConfig *config;
+  gint config_count;
+
+  g_debug ("vimeo_plugin_init\n");
+
+  if (!g_thread_supported ()) {
+    g_thread_init (NULL);
+  }
+
+  if (!configs) {
+    g_warning ("Missing configuration");
+    return FALSE;
+  }
+
+  config_count = g_list_length (configs);
+  if (config_count > 1) {
+    g_warning ("Provided %d configs, but will only use one", config_count);
+  }
+
+  config = GRL_CONFIG (configs->data);
+
+  vimeo_key = grl_config_get_api_key (config);
+  vimeo_secret = grl_config_get_api_secret (config);
+
+  if (!vimeo_key || !vimeo_secret) {
+    g_warning ("Required configuration keys not set up");
+    return FALSE;
+  }
+
+  GrlVimeoSource *source = grl_vimeo_source_new ();
+  source->priv->vimeo = g_vimeo_new (vimeo_key, vimeo_secret);
+
+  grl_plugin_registry_register_source (registry,
+                                       plugin,
+                                       GRL_MEDIA_PLUGIN (source));
+  return TRUE;
+}
+
+GRL_PLUGIN_REGISTER (grl_vimeo_plugin_init,
+                     NULL,
+                     PLUGIN_ID,
+                     PLUGIN_NAME,
+                     PLUGIN_DESC,
+                     PACKAGE_VERSION,
+                     AUTHOR,
+                     LICENSE,
+                     SITE);
+
+/* ================== Vimeo GObject ================ */
+
+static GrlVimeoSource *
+grl_vimeo_source_new (void)
+{
+  g_debug ("grl_vimeo_source_new");
+
+  return g_object_new (GRL_VIMEO_SOURCE_TYPE,
+                       "source-id", SOURCE_ID,
+                       "source-name", SOURCE_NAME,
+                       "source-desc", SOURCE_DESC,
+                       NULL);
+}
+
+static void
+grl_vimeo_source_class_init (GrlVimeoSourceClass * klass)
+{
+  GrlMediaSourceClass *source_class = GRL_MEDIA_SOURCE_CLASS (klass);
+  GrlMetadataSourceClass *metadata_class = GRL_METADATA_SOURCE_CLASS (klass);
+
+  source_class->metadata = grl_vimeo_source_metadata;
+  source_class->search = grl_vimeo_source_search;
+  metadata_class->supported_keys = grl_vimeo_source_supported_keys;
+
+  g_type_class_add_private (klass, sizeof (GrlVimeoSourcePrivate));
+}
+
+static void
+grl_vimeo_source_init (GrlVimeoSource *source)
+{
+  source->priv = GRL_VIMEO_SOURCE_GET_PRIVATE (source);
+}
+
+G_DEFINE_TYPE (GrlVimeoSource, grl_vimeo_source, GRL_TYPE_MEDIA_SOURCE);
+
+/* ======================= Utilities ==================== */
+
+static gint
+str_to_gint (gchar *str)
+{
+  errno = 0;
+  gint number = (gint) g_ascii_strtod (str, NULL);
+  if (errno == 0)
+  {
+    return number;
+  }
+  return 0;
+}
+
+static void
+update_media (GrlMedia *media, GHashTable *video)
+{
+  gchar *str;
+
+  str = g_hash_table_lookup (video, VIMEO_VIDEO_ID);
+  if (str)
+  {
+    grl_media_set_id (media, str);
+  }
+
+  str = g_hash_table_lookup (video, VIMEO_VIDEO_TITLE);
+  if (str)
+  {
+    grl_media_set_title (media, str);
+  }
+
+  str = g_hash_table_lookup (video, VIMEO_VIDEO_DESCRIPTION);
+  if (str)
+  {
+    grl_media_set_description (media, str);
+  }
+
+  str = g_hash_table_lookup (video, VIMEO_VIDEO_DURATION);
+  if (str)
+  {
+    grl_media_set_duration (media, str_to_gint (str));
+  }
+
+  str = g_hash_table_lookup (video, VIMEO_VIDEO_OWNER_NAME);
+  if (str)
+  {
+    grl_media_set_author (media, str);
+  }
+
+  str = g_hash_table_lookup (video, VIMEO_VIDEO_UPLOAD_DATE);
+  if (str)
+  {
+    grl_media_set_date (media, str);
+  }
+
+  str = g_hash_table_lookup (video, VIMEO_VIDEO_THUMBNAIL);
+  if (str)
+  {
+    grl_media_set_thumbnail (media, str);
+  }
+
+  str = g_hash_table_lookup (video, VIMEO_VIDEO_WIDTH);
+  if (str)
+  {
+    grl_media_video_set_width (media, str_to_gint (str));
+  }
+
+  str = g_hash_table_lookup (video, VIMEO_VIDEO_HEIGHT);
+  if (str)
+  {
+    grl_media_video_set_height (media, str_to_gint (str));
+  }
+}
+
+
+static void
+search_cb (GVimeo *vimeo, GList *video_list, gpointer user_data)
+{
+  GrlMedia *media = NULL;
+  SearchData *sd = (SearchData *) user_data;
+  gchar *media_type;
+
+  /* Go to offset element */
+  video_list = g_list_nth (video_list, sd->offset);
+
+  /* No more elements can be sent */
+  if (!video_list) {
+    sd->ss->callback (sd->ss->source,
+                      sd->ss->search_id,
+                      NULL,
+                      0,
+                      sd->ss->user_data,
+                      NULL);
+    g_free (sd);
+    return;
+  }
+
+  while (video_list && sd->ss->count)
+  {
+    media_type = g_hash_table_lookup (video_list->data, "title");
+    if (media_type) {
+      media = grl_media_video_new ();
+    }
+
+    if (media)
+    {
+      update_media (media, video_list->data);
+      sd->ss->callback (sd->ss->source,
+			sd->ss->search_id,
+			media,
+			sd->ss->count == 1? 0: -1,
+			sd->ss->user_data,
+			NULL);
+    }
+    video_list = g_list_next (video_list);
+    sd->ss->count--;
+    media = NULL;
+  }
+
+  /* Get more elements */
+  if (sd->ss->count)
+  {
+    sd->offset = 0;
+    sd->page++;
+    g_vimeo_videos_search (vimeo, sd->ss->text, sd->page, search_cb, sd);
+  }
+  else
+  {
+    g_free (sd);
+  }
+}
+
+static void
+video_get_play_url_cb (gchar *url, gpointer user_data)
+{
+  GrlMediaSourceMetadataSpec *ms = (GrlMediaSourceMetadataSpec *) user_data;
+
+  if (url)
+  {
+    grl_media_set_url (ms->media, url);
+  }
+
+  ms->callback (ms->source, ms->media, ms->user_data, NULL);
+}
+
+/* ================== API Implementation ================ */
+
+static const GList *
+grl_vimeo_source_supported_keys (GrlMetadataSource *source)
+{
+  static GList *keys = NULL;
+  if (!keys) {
+    keys = grl_metadata_key_list_new (GRL_METADATA_KEY_ID,
+				      GRL_METADATA_KEY_TITLE,
+				      GRL_METADATA_KEY_DESCRIPTION,
+				      GRL_METADATA_KEY_URL,
+				      GRL_METADATA_KEY_AUTHOR,
+                                      GRL_METADATA_KEY_DATE,
+				      GRL_METADATA_KEY_THUMBNAIL,
+				      GRL_METADATA_KEY_DURATION,
+				      GRL_METADATA_KEY_WIDTH,
+				      GRL_METADATA_KEY_HEIGHT,
+				      NULL);
+  }
+  return keys;
+}
+
+static void
+grl_vimeo_source_metadata (GrlMediaSource *source,
+			   GrlMediaSourceMetadataSpec *ms)
+{
+  const gchar *id_str;
+
+  if (!ms->media || (id_str = grl_media_get_id (ms->media)) == NULL)
+  {
+    ms->callback (ms->source, ms->media, ms->user_data, NULL);
+    return;
+  }
+
+  errno = 0;
+  gint id = (gint) g_ascii_strtod (id_str, NULL);
+  if (errno != 0)
+  {
+    return;
+  }
+
+  g_vimeo_video_get_play_url (GRL_VIMEO_SOURCE (source)->priv->vimeo,
+			      id,
+			      video_get_play_url_cb,
+			      ms);
+}
+
+static void
+grl_vimeo_source_search (GrlMediaSource *source,
+			 GrlMediaSourceSearchSpec *ss)
+{
+  GVimeo *vimeo = GRL_VIMEO_SOURCE (source)->priv->vimeo;
+  gint per_page;
+
+  /* Compute items per page and page offset */
+  per_page = CLAMP (1 + ss->skip + ss->count, 0, 100);
+  g_vimeo_set_per_page (vimeo, 50);
+
+  SearchData *sd = g_new (SearchData, 1);
+  sd->page = 1 + (ss->skip / per_page);
+  sd->offset = ss->skip % per_page;
+  sd->ss = ss;
+
+  g_vimeo_videos_search (vimeo, ss->text, sd->page, search_cb, sd);
+}
diff --git a/src/vimeo/grl-vimeo.h b/src/vimeo/grl-vimeo.h
new file mode 100644
index 0000000..fde4f36
--- /dev/null
+++ b/src/vimeo/grl-vimeo.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2010 Igalia S.L.
+ *
+ * Contact: Iago Toral Quiroga <itoral igalia com>
+ *
+ * Authors: Joaquim Rocha <jrocha igalia com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef _GRL_VIMEO_SOURCE_H_
+#define _GRL_VIMEO_SOURCE_H_
+
+#include <grilo.h>
+
+#define GRL_VIMEO_SOURCE_TYPE                  \
+  (grl_vimeo_source_get_type ())
+
+#define GRL_VIMEO_SOURCE(obj)                          \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj),                   \
+                               GRL_VIMEO_SOURCE_TYPE,  \
+                               GrlVimeoSource))
+
+#define GRL_IS_VIMEO_SOURCE(obj)                       \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj),                   \
+                               GRL_VIMEO_SOURCE_TYPE))
+
+#define GRL_VIMEO_SOURCE_CLASS(klass)                  \
+  (G_TYPE_CHECK_CLASS_CAST((klass),                     \
+                           GRL_VIMEO_SOURCE_TYPE,      \
+                           GrlVimeoSourceClass))
+
+#define GRL_IS_VIMEO_SOURCE_CLASS(klass)               \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),                     \
+                           GRL_VIMEO_SOURCE_TYPE))
+
+#define GRL_VIMEO_SOURCE_GET_CLASS(obj)                \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj),                    \
+                              GRL_VIMEO_SOURCE_TYPE,   \
+                              GrlVimeoSourceClass))
+
+typedef struct _GrlVimeoSource        GrlVimeoSource;
+typedef struct _GrlVimeoSourcePrivate GrlVimeoSourcePrivate;
+
+struct _GrlVimeoSource {
+
+  GrlMediaSource parent;
+
+  /*< private >*/
+  GrlVimeoSourcePrivate *priv;
+};
+
+typedef struct _GrlVimeoSourceClass GrlVimeoSourceClass;
+
+struct _GrlVimeoSourceClass {
+
+  GrlMediaSourceClass parent_class;
+
+};
+
+GType grl_vimeo_source_get_type (void);
+
+#endif /* _GRL_VIMEO_SOURCE_H_ */
diff --git a/src/vimeo/gvimeo.c b/src/vimeo/gvimeo.c
new file mode 100644
index 0000000..389247c
--- /dev/null
+++ b/src/vimeo/gvimeo.c
@@ -0,0 +1,518 @@
+/*
+ * Copyright (C) 2010 Igalia S.L.
+ *
+ * Contact: Iago Toral Quiroga <itoral igalia com>
+ *
+ * Authors: Joaquim Rocha <jrocha igalia com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <gcrypt.h>
+#include <libsoup/soup.h>
+#include "gvimeo.h"
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+
+#define G_VIMEO_GET_PRIVATE(object)             \
+  (G_TYPE_INSTANCE_GET_PRIVATE((object),        \
+                               G_VIMEO_TYPE,    \
+                               GVimeoPrivate))
+
+#define PLUGIN_USER_AGENT             "Grilo Vimeo Plugin"
+
+#define VIMEO_ENDPOINT                "http://vimeo.com/api/rest/v2";
+#define VIMEO_VIDEO_LOAD_URL          "http://vimeo.com/moogaloop/load/clip:";
+#define VIMEO_VIDEO_PLAY_URL          "http://vimeo.com/moogaloop/play/clip:";
+
+#define VIMEO_VIDEO_SEARCH_METHOD     "vimeo.videos.search"
+#define VIMEO_API_OAUTH_SIGN_METHOD   "HMAC-SHA1"
+#define VIMEO_API_OAUTH_SIGNATURE_PARAM "&oauth_signature=%s"
+
+#define VIMEO_VIDEO_SEARCH					\
+  "full_response=yes"						\
+  "&method=%s"							\
+  "&oauth_consumer_key=%s"					\
+  "&oauth_nonce=%s"						\
+  "&oauth_signature_method=" VIMEO_API_OAUTH_SIGN_METHOD	\
+  "&oauth_timestamp=%s"						\
+  "&oauth_token="						\
+  "&page=%d"							\
+  "&per_page=%d"						\
+  "&query=%s"
+
+typedef struct {
+  GVimeo *vimeo;
+  GVimeoVideoSearchCb search_cb;
+  gpointer user_data;
+} GVimeoVideoSearchData;
+
+typedef struct {
+  GVimeo *vimeo;
+  gint video_id;
+  GVimeoURLCb callback;
+  gpointer user_data;
+} GVimeoVideoURLData;
+
+struct _GVimeoPrivate {
+  gchar *api_key;
+  gchar *auth_token;
+  gchar *auth_secret;
+  gint per_page;
+  SoupSession *async_session;
+};
+
+enum InfoType {SIMPLE, EXTENDED};
+
+typedef struct { 
+  enum InfoType type;
+  gchar *name;
+} VideoInfo;
+
+static VideoInfo video_info[] = {{SIMPLE, VIMEO_VIDEO_TITLE},
+				 {SIMPLE, VIMEO_VIDEO_DESCRIPTION},
+				 {SIMPLE, VIMEO_VIDEO_UPLOAD_DATE},
+				 {SIMPLE, VIMEO_VIDEO_WIDTH},
+				 {SIMPLE, VIMEO_VIDEO_HEIGHT},
+				 {SIMPLE, VIMEO_VIDEO_OWNER},
+				 {SIMPLE, VIMEO_VIDEO_URL},
+				 {SIMPLE, VIMEO_VIDEO_THUMBNAIL},
+				 {SIMPLE, VIMEO_VIDEO_DURATION},
+				 {EXTENDED, VIMEO_VIDEO_OWNER}};
+
+static void g_vimeo_finalize (GObject *object);
+
+/* -------------------- GOBJECT -------------------- */
+
+G_DEFINE_TYPE (GVimeo, g_vimeo, G_TYPE_OBJECT);
+
+static void
+g_vimeo_class_init (GVimeoClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->finalize = g_vimeo_finalize;
+
+  g_type_class_add_private (klass, sizeof (GVimeoPrivate));
+}
+
+static void
+g_vimeo_init (GVimeo *vimeo)
+{
+  vimeo->priv = G_VIMEO_GET_PRIVATE (vimeo);
+  vimeo->priv->per_page = 50;
+  vimeo->priv->async_session = soup_session_async_new ();
+}
+
+static void
+g_vimeo_finalize (GObject *object)
+{
+  GVimeo *vimeo = G_VIMEO (object);
+  g_free (vimeo->priv->api_key);
+  g_object_unref (vimeo->priv->async_session);
+
+  G_OBJECT_CLASS (g_vimeo_parent_class)->finalize (object);
+}
+
+GVimeo *
+g_vimeo_new (const gchar *api_key, const gchar *auth_secret)
+{
+  GVimeo *vimeo = g_object_new (G_VIMEO_TYPE, NULL);
+  vimeo->priv->api_key = g_strdup (api_key);
+  vimeo->priv->auth_secret = g_strdup (auth_secret);
+
+  return vimeo;
+}
+
+/* -------------------- PRIVATE API -------------------- */
+
+static gchar *
+get_timestamp (void)
+{
+  time_t t = time (NULL);
+  return g_strdup_printf ("%d", (gint) t);
+}
+
+static gchar *
+get_nonce (void)
+{
+  gchar *timestamp = get_timestamp();
+  guint rnd_number = g_random_int ();
+  gchar *rnd_str = g_strdup_printf ("%d_%s", rnd_number, timestamp);
+  gchar *nonce = g_compute_checksum_for_string (G_CHECKSUM_MD5, rnd_str, -1);
+  g_free (timestamp);
+  g_free (rnd_str);
+
+  return nonce;
+}
+
+static gchar *
+get_videos_search_params (GVimeo *vimeo, const gchar *text, gint page) {
+  gchar *timestamp = get_timestamp ();
+  gchar *nonce = get_nonce ();
+  gchar *params = g_strdup_printf (VIMEO_VIDEO_SEARCH,
+				   VIMEO_VIDEO_SEARCH_METHOD,
+				   vimeo->priv->api_key,
+				   nonce,
+				   timestamp,
+				   page,
+				   vimeo->priv->per_page,
+				   text);
+  g_free (timestamp);
+  g_free (nonce);
+
+  return params;
+}
+
+static gchar *
+sign_string (gchar *message, gchar *key)
+{
+  gchar *signed_message = NULL;
+  gcry_md_hd_t digest_obj;
+  unsigned char *hmac_digest;
+
+  gcry_md_open(&digest_obj,
+	       GCRY_MD_SHA1,
+	       GCRY_MD_FLAG_SECURE | GCRY_MD_FLAG_HMAC);
+  gcry_md_setkey(digest_obj, key, strlen (key));
+  gcry_md_write (digest_obj, message, strlen (message));
+  gcry_md_final (digest_obj);
+  hmac_digest = gcry_md_read (digest_obj, 0);
+
+  signed_message = g_base64_encode (hmac_digest,
+				    strlen ((gchar *) hmac_digest));
+
+  gcry_md_close (digest_obj);
+
+  return signed_message;
+}
+
+static void
+skip_garbage_nodes (xmlNodePtr *node)
+{
+  /* Result contains "\n" and "\t" to pretty align XML. Unfortunately, libxml
+     doesn't cope very fine with them, and it creates "fakes" nodes with name
+     "text" and value those characters. So we need to skip them */
+  while ((*node) && xmlStrcmp ((*node)->name, (const xmlChar *) "text") == 0)
+  {
+    (*node) = (*node)->next;
+  }
+}
+
+static gboolean
+result_is_correct (xmlNodePtr node)
+{
+  gboolean correct = FALSE;
+  xmlChar *stat;
+
+  if (xmlStrcmp (node->name, (const xmlChar *) "rsp") == 0)
+  {
+    stat = xmlGetProp (node, (const xmlChar *) "stat");
+    if (stat && xmlStrcmp (stat, (const xmlChar *) "ok") == 0)
+    {
+      correct = TRUE;
+      xmlFree (stat);
+    }
+  }
+
+  return correct;
+}
+
+static void
+add_node (xmlNodePtr node, GHashTable *video)
+{
+  xmlAttrPtr attr;
+
+  for (attr = node->properties; attr != NULL; attr = attr->next)
+  {
+    g_hash_table_insert (video,
+                         g_strconcat ((const gchar *) node->name,
+                                      "_",
+                                      (const gchar *) attr->name,
+                                      NULL),
+                         (gchar *) xmlGetProp (node, attr->name));
+  }
+}
+
+static xmlNodePtr
+xpath_get_node (xmlXPathContextPtr context, gchar *xpath_expr)
+{
+  xmlNodePtr node = NULL;
+  xmlXPathObjectPtr xpath_obj;
+  xpath_obj = xmlXPathEvalExpression ((xmlChar *) xpath_expr, context);
+
+  if (xpath_obj && xpath_obj->nodesetval->nodeTab)
+  {
+    node = xpath_obj->nodesetval->nodeTab[0];
+  }
+  xmlXPathFreeObject (xpath_obj);
+
+  return node;
+}
+
+static gchar *
+get_node_text (xmlXPathContextPtr context, gchar *xpath_expr)
+{
+  xmlNodePtr node;
+  gchar *node_text = NULL;
+
+  node = xpath_get_node (context, xpath_expr);
+  if (node)
+  {
+    node_text = (gchar *) xmlNodeGetContent (node);
+  }
+
+  return node_text;
+}
+
+static GHashTable *
+get_video (xmlNodePtr node)
+{
+  xmlXPathContextPtr context;
+  gchar *video_id;
+  GHashTable *video = g_hash_table_new_full (g_str_hash,
+                                             g_str_equal,
+                                             g_free,
+                                             g_free);
+
+  /* Adds the video node's properties */
+  add_node (node, video);
+
+  context = xmlXPathNewContext (node->doc);
+  video_id = (gchar *) xmlGetProp (node, (xmlChar *) "id");
+
+  gint i;
+  gint array_length  = G_N_ELEMENTS (video_info);
+  for (i = 0; i < array_length; i++)
+  {
+    /* Look for the wanted information only under the current video */
+    gchar *xpath_name = g_strdup_printf ("//video[ id=%s]//%s",
+					 video_id,
+					 video_info[i].name);
+    xmlNodePtr info_node = xpath_get_node (context, xpath_name);
+    if (info_node)
+    {
+      if (video_info[i].type == EXTENDED) {
+	add_node (info_node, video);
+      }
+      else
+      {
+	g_hash_table_insert (video,
+			     g_strdup ((const gchar *) info_node->name),
+			     (gchar *) xmlNodeGetContent (info_node));
+      }
+    }
+    g_free (xpath_name);
+  }
+  g_free (video_id);
+
+  xmlXPathFreeContext (context);
+
+  return video;
+}
+
+
+static void
+process_video_search_result (const gchar *xml_result, gpointer user_data)
+{
+  xmlDocPtr doc;
+  xmlNodePtr node;
+  GList *video_list = NULL;
+  GVimeoVideoSearchData *data = (GVimeoVideoSearchData *) user_data;
+
+  doc = xmlRecoverDoc ((xmlChar *) xml_result);
+  node = xmlDocGetRootElement (doc);
+
+  /* Check result is ok */
+  if (!node || !result_is_correct (node))
+  {
+    data->search_cb (data->vimeo, NULL, data->user_data);
+  }
+  else
+  {
+    node = node->xmlChildrenNode;
+    skip_garbage_nodes (&node);
+
+    /* Now we're at "video pages" node */
+    node = node->xmlChildrenNode;
+    skip_garbage_nodes (&node);
+    while (node)
+    {
+      video_list = g_list_prepend (video_list, get_video (node));
+      node = node->next;
+      skip_garbage_nodes (&node);
+    }
+
+    data->search_cb (data->vimeo, g_list_reverse (video_list), data->user_data);
+    g_list_foreach (video_list, (GFunc) g_hash_table_unref, NULL);
+    g_list_free (video_list);
+  }
+  g_free (data);
+  xmlFreeDoc (doc);
+}
+
+static void
+search_videos_complete_cb (SoupSession *session,
+			   SoupMessage *message,
+			   gpointer *data)
+{
+  GVimeoVideoSearchCb *search_data = (GVimeoVideoSearchCb *) data;
+  process_video_search_result (message->response_body->data, search_data);
+}
+
+static gchar *
+get_play_url_from_vimeo_xml (const gchar *xml, gint video_id)
+{
+  xmlDocPtr doc = xmlRecoverDoc ((xmlChar *) xml);
+  xmlXPathContextPtr context = xmlXPathNewContext (doc);
+  gchar *request_signature = get_node_text (context,
+					    "/xml/request_signature[1]");
+  gchar *signature_expires = get_node_text (context,
+					    "/xml/request_signature_expires[1]");
+
+  gchar *url = g_strdup_printf ("%s%d/%s/%s/?q=sd",
+				VIMEO_VIDEO_PLAY_URL,
+			        video_id,
+				request_signature,
+				signature_expires);
+
+  g_free (request_signature);
+  g_free (signature_expires);
+  xmlXPathFreeContext (context);
+  xmlFreeDoc (doc);
+
+  return url;
+}
+
+static void
+get_video_play_url_complete_cb (SoupSession *session,
+				SoupMessage *message,
+				gpointer *data)
+{
+  if (message->response_body == NULL)
+  {
+    return;
+  }
+
+  GVimeoVideoURLData *url_data = (GVimeoVideoURLData *) data;
+  gchar *url =  get_play_url_from_vimeo_xml (message->response_body->data,
+					     url_data->video_id);
+  url_data->callback (url, url_data->user_data);
+  g_free (url_data);
+}
+
+static gchar *
+encode_uri (gchar *uri)
+{
+  return soup_uri_encode (uri, "%!*'();:@&=+$,/?#[] ");
+}
+
+static gchar *
+build_request (GVimeo *vimeo, const gchar *query, gint page)
+{
+  g_return_val_if_fail (G_IS_VIMEO (vimeo), NULL);
+
+  gchar *params;
+  gchar *endpoint_encoded;
+  gchar *key;
+  gchar *escaped_str;
+  gchar *tmp_str;
+  gchar *signature;
+
+  params = get_videos_search_params (vimeo, query, page);
+  endpoint_encoded = encode_uri (VIMEO_ENDPOINT);
+  key = g_strdup_printf ("%s&", vimeo->priv->auth_secret);
+  escaped_str = encode_uri (params);
+  tmp_str = g_strdup_printf ("GET&%s&%s", endpoint_encoded, escaped_str);
+  g_free (endpoint_encoded);
+  signature = sign_string (tmp_str, key);
+  g_free (escaped_str);
+  g_free (tmp_str);
+  escaped_str = encode_uri (signature);
+  tmp_str = g_strdup_printf ("%s?%s" VIMEO_API_OAUTH_SIGNATURE_PARAM,
+			     VIMEO_ENDPOINT,
+			     params,
+			     escaped_str);
+
+  g_free (params);
+  g_free (key);
+  g_free (escaped_str);
+  g_free (signature);
+
+  return tmp_str;
+}
+
+/* -------------------- PUBLIC API -------------------- */
+
+void
+g_vimeo_set_per_page (GVimeo *vimeo, gint per_page)
+{
+  g_return_if_fail (G_IS_VIMEO (vimeo));
+  vimeo->priv->per_page = per_page;
+}
+
+void
+g_vimeo_videos_search (GVimeo *vimeo,
+		       const gchar *text,
+		       gint page,
+		       GVimeoVideoSearchCb callback,
+		       gpointer user_data)
+{
+  g_return_if_fail (G_IS_VIMEO (vimeo));
+
+  SoupMessage *message;
+  GVimeoVideoSearchData *search_data;
+  gchar *request = build_request (vimeo, text, page);
+
+  search_data = g_new (GVimeoVideoSearchData, 1);
+  search_data->vimeo = vimeo;
+  search_data->search_cb = callback;
+  search_data->user_data = user_data;
+
+  message = soup_message_new ("GET", request);
+  soup_session_queue_message (vimeo->priv->async_session,
+			      message,
+			      (SoupSessionCallback) search_videos_complete_cb,
+			      search_data);
+  g_free (request);
+}
+
+void
+g_vimeo_video_get_play_url (GVimeo *vimeo,
+			    gint id,
+			    GVimeoURLCb callback,
+			    gpointer user_data)
+{
+  GVimeoVideoURLData *data;
+  gchar *url = g_strdup_printf ("%s%d",
+				VIMEO_VIDEO_LOAD_URL,
+				id);
+  SoupMessage *message = soup_message_new ("GET", url);
+  SoupMessageHeaders *headers = message->request_headers;
+  soup_message_headers_append (headers, "User-Agent", PLUGIN_USER_AGENT);
+
+  data = g_new (GVimeoVideoURLData, 1);
+  data->video_id = id;
+  data->vimeo = vimeo;
+  data->callback = callback;
+  data->user_data = user_data;
+
+  soup_session_queue_message (vimeo->priv->async_session,
+			      message,
+			      (SoupSessionCallback) get_video_play_url_complete_cb,
+			      data);
+  g_free (url);
+}
diff --git a/src/vimeo/gvimeo.h b/src/vimeo/gvimeo.h
new file mode 100644
index 0000000..34e17a3
--- /dev/null
+++ b/src/vimeo/gvimeo.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2010 Igalia S.L.
+ *
+ * Contact: Iago Toral Quiroga <itoral igalia com>
+ *
+ * Authors: Joaquim Rocha <jrocha igalia com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * 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 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef _G_VIMEO_H_
+#define _G_VIMEO_H_
+
+#include <glib-object.h>
+
+#define G_VIMEO_TYPE                           \
+  (g_vimeo_get_type ())
+
+#define G_VIMEO(obj)                                   \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj),                   \
+                               G_VIMEO_TYPE,           \
+                               GVimeo))
+
+#define G_IS_VIMEO(obj)                                \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj),                   \
+                               G_VIMEO_TYPE))
+
+#define G_VIMEO_CLASS(klass)                           \
+  (G_TYPE_CHECK_CLASS_CAST((klass),                     \
+                           G_VIMEO_TYPE,               \
+                           GVimeoClass))
+
+#define G_IS_VIMEO_CLASS(klass)                        \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),                     \
+                           G_VIMEO_TYPE))
+
+#define G_VIMEO_GET_CLASS(obj)                         \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj),                    \
+                              G_VIMEO_TYPE,            \
+                              GVimeoClass))
+
+#define VIMEO_VIDEO "video"
+#define VIMEO_VIDEO_ID VIMEO_VIDEO "_id"
+#define VIMEO_VIDEO_TITLE "title"
+#define VIMEO_VIDEO_DESCRIPTION "description"
+#define VIMEO_VIDEO_URL "url"
+#define VIMEO_VIDEO_UPLOAD_DATE "upload_date"
+#define VIMEO_VIDEO_WIDTH "width"
+#define VIMEO_VIDEO_HEIGHT "height"
+#define VIMEO_VIDEO_DURATION "duration"
+#define VIMEO_VIDEO_OWNER "owner"
+#define VIMEO_VIDEO_THUMBNAIL "thumbnail"
+
+#define VIMEO_VIDEO_OWNER_NAME VIMEO_VIDEO_OWNER "_realname"
+
+typedef struct _GVimeo        GVimeo;
+typedef struct _GVimeoPrivate GVimeoPrivate;
+
+struct _GVimeo {
+
+  GObject parent;
+
+  /*< private >*/
+  GVimeoPrivate *priv;
+};
+
+typedef struct _GVimeoClass GVimeoClass;
+
+struct _GVimeoClass {
+
+  GObjectClass parent_class;
+
+};
+
+typedef void (*GVimeoVideoSearchCb) (GVimeo *vimeo,
+				     GList *videolist,
+				     gpointer user_data);
+
+typedef void (*GVimeoURLCb) (gchar *url, gpointer user_data);
+
+GType g_vimeo_get_type (void);
+
+GVimeo *g_vimeo_new (const gchar *api_key, const gchar *auth_secret);
+
+void g_vimeo_video_get_play_url (GVimeo *vimeo,
+				 gint id,
+				 GVimeoURLCb callback,
+				 gpointer user_data);
+
+void g_vimeo_set_per_page (GVimeo *vimeo, gint per_page);
+
+void g_vimeo_videos_search (GVimeo *vimeo,
+			    const gchar *text,
+			    gint page,
+			    GVimeoVideoSearchCb callback,
+			    gpointer user_data);
+
+#endif /* _G_VIMEO_H_ */
-- 
1.6.3.3



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