[grilo-plugins] freebox: Add new Freebox TV source



commit 7a8287c37c3da7480a4ed25fa3313900f663bdd2
Author: Bastien Nocera <hadess hadess net>
Date:   Fri Dec 13 23:30:12 2013 +0100

    freebox: Add new Freebox TV source
    
    This source will show a list of TV channels available through
    the Freebox server, offered by the Free ISP in France.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=720436

 configure.ac                      |   53 ++++++
 src/Makefile.am                   |    6 +-
 src/freebox/Makefile.am           |   52 ++++++
 src/freebox/free.png              |  Bin 0 -> 11860 bytes
 src/freebox/freebox-monitor.c     |  162 ++++++++++++++++++
 src/freebox/freebox-monitor.h     |   68 ++++++++
 src/freebox/freebox.gresource.xml |    6 +
 src/freebox/grl-freebox.c         |  330 +++++++++++++++++++++++++++++++++++++
 src/freebox/grl-freebox.h         |   76 +++++++++
 src/freebox/grl-freebox.xml       |   10 +
 10 files changed, 762 insertions(+), 1 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index d77fdc3..6fd44c4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -39,6 +39,9 @@ AM_PROG_CC_C_O
 
 AC_HEADER_STDC
 
+m4_define([glib_required_version], [2.32.0])
+AM_PATH_GLIB_2_0(glib_required_version)
+
 m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
 
 # ----------------------------------------------------------
@@ -112,6 +115,9 @@ AC_DEFINE_UNQUOTED(GRL_PLUGINS_DIR, "$GRL_PLUGINS_DIR", [Plugins directory])
 PKG_CHECK_MODULES([GRLNET], [${GRLNET_NAME} >= 0.2.2],
                            HAVE_GRLNET=yes,
                            HAVE_GRLNET=no)
+PKG_CHECK_MODULES([GRLPLS], [${GRLPLS_NAME} >= 0.2.0],
+                           HAVE_GRLPLS=yes,
+                           HAVE_GRLPLS=no)
 
 PKG_CHECK_MODULES([GRLPLS], [${GRLPLS_NAME} >= 0.2.0],
                            HAVE_GRLPLS=yes,
@@ -150,6 +156,9 @@ PKG_CHECK_MODULES(GMIME, gmime-2.6,
                          [PKG_CHECK_MODULES(GMIME, gmime-2.4,
                                                    HAVE_GMIME=yes,
                                                    HAVE_GMIME=no)])
+
+PKG_CHECK_MODULES(AVAHI, avahi-gobject, HAVE_AVAHI=yes, HAVE_AVAHI=no)
+
 # Search available Tracker version
 trackerpkg="tracker-sparql-0.10"
 m4_foreach([VERSION], [[0.10], [0.11], [0.12], [0.14], [0.15], [0.16], [0.17], [0.18]],
@@ -1126,6 +1135,49 @@ DEPS_TMDB_LIBS="$DEPS_LIBS $LIBSOUP_LIBS $GRLNET_LIBS $JSON_LIBS"
 AC_SUBST(DEPS_TMDB_LIBS)
 
 # ----------------------------------------------------------
+# BUILD FREEBOX PLUGIN
+# ----------------------------------------------------------
+
+AC_ARG_ENABLE(freebox,
+        AC_HELP_STRING([--enable-freebox],
+                [enable Freebox plugin (default: auto)]),
+        [
+                case "$enableval" in
+                     yes)
+                        if test "x$HAVE_GRLPLS" = "xno"; then
+                           AC_MSG_ERROR([grilo-pls-0.2 >= 0.2.0 not found, install it or use 
--disable-freebox])
+                        fi
+                        if test "x$HAVE_AVAHI" = "xno"; then
+                           AC_MSG_ERROR([avahi not found, install it or use --disable-freebox])
+                        fi
+                        ;;
+                esac
+        ],
+        [
+               if test "x$HAVE_GRLPLS" = "xyes" -a "x$HAVE_AVAHI" = "xyes"; then
+                  enable_freebox=yes
+               else
+                  enable_freebox=no
+               fi
+        ])
+
+AM_CONDITIONAL([FREEBOX_PLUGIN], [test "x$enable_freebox" = "xyes"])
+GRL_PLUGINS_ALL="$GRL_PLUGINS_ALL freebox"
+if test "x$enable_freebox" = "xyes"
+then
+       GRL_PLUGINS_ENABLED="$GRL_PLUGINS_ENABLED freebox"
+fi
+
+FREEBOX_PLUGIN_ID="grl-freebox"
+AC_SUBST(FREEBOX_PLUGIN_ID)
+AC_DEFINE_UNQUOTED([FREEBOX_PLUGIN_ID], ["$FREEBOX_PLUGIN_ID"], [Freebox plugin ID])
+
+DEPS_FREEBOX_CFLAGS="$DEPS_CFLAGS $GRLPLS_CFLAGS $AVAHI_CFLAGS"
+AC_SUBST(DEPS_FREEBOX_CFLAGS)
+DEPS_FREEBOX_LIBS="$DEPS_LIBS $GRLPLS_LIBS $AVAHI_LIBS"
+AC_SUBST(DEPS_FREEBOX_LIBS)
+
+# ----------------------------------------------------------
 # GETTEXT/INTLTOOL
 # ----------------------------------------------------------
 
@@ -1166,6 +1218,7 @@ AC_CONFIG_FILES([
   src/dmap/Makefile
   src/filesystem/Makefile
   src/flickr/Makefile
+  src/freebox/Makefile
   src/gravatar/Makefile
   src/jamendo/Makefile
   src/lastfm-albumart/Makefile
diff --git a/src/Makefile.am b/src/Makefile.am
index 83bbca9..d4d763a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -94,8 +94,12 @@ if YOUTUBE_PLUGIN
 SUBDIRS += youtube
 endif
 
+if FREEBOX_PLUGIN
+SUBDIRS += freebox
+endif
+
 DIST_SUBDIRS = \
-   apple-trailers bliptv bookmarks dmap filesystem flickr gravatar jamendo \
+   apple-trailers bliptv bookmarks dmap filesystem flickr freebox gravatar jamendo \
    lastfm-albumart local-metadata magnatune metadata-store optical-media   \
    podcasts raitv shoutcast tmdb tracker upnp vimeo youtube
 
diff --git a/src/freebox/Makefile.am b/src/freebox/Makefile.am
new file mode 100644
index 0000000..b91f4c8
--- /dev/null
+++ b/src/freebox/Makefile.am
@@ -0,0 +1,52 @@
+#
+# Makefile.am
+#
+# Author: Bastien Nocera <hadess hadess net>
+#
+# Copyright (C) 2013 Bastien Nocera
+
+include $(top_srcdir)/gtester.mk
+
+ext_LTLIBRARIES                 = libgrlfreebox.la
+
+libgrlfreebox_la_CFLAGS =                      \
+       $(DEPS_FREEBOX_CFLAGS)  \
+       -DG_LOG_DOMAIN=\"GrlFreebox\"   \
+       -DLOCALEDIR=\"$(localedir)\"
+
+libgrlfreebox_la_LIBADD =      \
+       $(DEPS_FREEBOX_LIBS)
+
+libgrlfreebox_la_LDFLAGS = \
+       -no-undefined         \
+       -module               \
+       -avoid-version
+
+libgrlfreebox_la_SOURCES =     \
+       grl-freebox.c           \
+       grl-freebox.h           \
+       freebox-monitor.h       \
+       freebox-monitor.c       \
+       freeboxresources.h      \
+       freeboxresources.c
+
+freeboxresources.h: freebox.gresource.xml
+       $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) $(srcdir)/freebox.gresource.xml \
+               --target=$@ --sourcedir=$(srcdir) --c-name _grl_freebox --generate-header
+freeboxresources.c: freebox.gresource.xml freeboxresources.h free.png
+       $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) $(srcdir)/freebox.gresource.xml \
+               --target=$@ --sourcedir=$(srcdir) --c-name _grl_freebox --generate-source
+
+extdir         = $(GRL_PLUGINS_DIR)
+freeboxxmldir  = $(GRL_PLUGINS_DIR)
+freeboxxml_DATA        = $(FREEBOX_PLUGIN_ID).xml
+
+EXTRA_DIST = $(freeboxxml_DATA) free.png freebox.gresource.xml
+
+MAINTAINERCLEANFILES = \
+       *.in            \
+       *~
+
+CLEANFILES = freeboxresources.h freeboxresources.c
+
+DISTCLEANFILES = $(MAINTAINERCLEANFILES)
diff --git a/src/freebox/free.png b/src/freebox/free.png
new file mode 100644
index 0000000..b8de787
Binary files /dev/null and b/src/freebox/free.png differ
diff --git a/src/freebox/freebox-monitor.c b/src/freebox/freebox-monitor.c
new file mode 100644
index 0000000..a436bf4
--- /dev/null
+++ b/src/freebox/freebox-monitor.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2011 Intel Corp
+ * Copyright (C) 2013 Bastien Nocera <hadess hadess net>
+ *
+ * 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; either version 2 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 package; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+#include <avahi-client/client.h>
+#include <avahi-client/lookup.h>
+#include <avahi-glib/glib-watch.h>
+#include <avahi-common/simple-watch.h>
+
+#include "freebox-monitor.h"
+
+struct _FreeboxMonitorPrivate {
+  /* Avahi <-> GLib adaptors */
+  AvahiGLibPoll *poll;
+  /* Avahi client */
+  AvahiClient *client;
+  /* Service browser */
+  AvahiServiceBrowser *browser;
+};
+
+#define GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), FREEBOX_TYPE_MONITOR, FreeboxMonitorPrivate))
+
+G_DEFINE_TYPE (FreeboxMonitor, freebox_monitor, G_TYPE_OBJECT);
+
+enum {
+  FOUND,
+  LOST,
+  NUM_SIGS
+};
+
+static guint signals[NUM_SIGS] = {0,};
+
+static void
+on_browse_callback (AvahiServiceBrowser *b,
+                    AvahiIfIndex interface, AvahiProtocol protocol,
+                    AvahiBrowserEvent event,
+                    const char *name,
+                    const char *type,
+                    const char *domain,
+                    AvahiLookupResultFlags flags,
+                    void* userdata)
+{
+  FreeboxMonitor *self = FREEBOX_MONITOR (userdata);
+
+  switch (event) {
+  case AVAHI_BROWSER_NEW:
+    /* Emit the found signal */
+    g_signal_emit (self, signals[FOUND], 0, name);
+    break;
+  case AVAHI_BROWSER_REMOVE:
+    /* Emit the lost signal */
+    g_signal_emit (self, signals[LOST], 0, name);
+    break;
+  default:
+    /* Nothing */
+    ;
+  }
+}
+
+static void
+on_client_state_changed (AvahiClient *client, AvahiClientState state, void *user_data)
+{
+  FreeboxMonitor *self = FREEBOX_MONITOR (user_data);
+  FreeboxMonitorPrivate *priv = self->priv;
+
+  switch (state) {
+  case AVAHI_CLIENT_S_RUNNING:
+    {
+      priv->browser = avahi_service_browser_new (client,
+                                                 AVAHI_IF_UNSPEC,
+                                                 AVAHI_PROTO_UNSPEC,
+                                                 "_fbx-api._tcp",
+                                                 NULL, 0,
+                                                 on_browse_callback, self);
+    }
+    break;
+  case AVAHI_CLIENT_S_REGISTERING:
+  case AVAHI_CLIENT_CONNECTING:
+    /* Silently do nothing */
+    break;
+  case AVAHI_CLIENT_S_COLLISION:
+  case AVAHI_CLIENT_FAILURE:
+  default:
+    g_warning ("Cannot connect to Avahi: state %d", state);
+    break;
+  }
+}
+
+static void
+freebox_monitor_finalize (GObject *object)
+{
+  FreeboxMonitorPrivate *priv = FREEBOX_MONITOR(object)->priv;
+
+  g_clear_pointer (&priv->browser, avahi_service_browser_free);
+  g_clear_pointer (&priv->client, avahi_client_free);
+  g_clear_pointer (&priv->poll, avahi_simple_poll_free);
+}
+
+static void
+freebox_monitor_class_init (FreeboxMonitorClass *klass)
+{
+    GObjectClass *o_class = (GObjectClass *)klass;
+
+    g_type_class_add_private (klass, sizeof (FreeboxMonitorPrivate));
+
+    o_class->finalize = freebox_monitor_finalize;
+
+    signals[FOUND] = g_signal_new ("found",
+                                   FREEBOX_TYPE_MONITOR,
+                                   G_SIGNAL_RUN_FIRST,
+                                   0, NULL, NULL,
+                                   g_cclosure_marshal_VOID__STRING,
+                                   G_TYPE_NONE,
+                                   1, G_TYPE_STRING);
+
+    signals[LOST] = g_signal_new ("lost",
+                                  FREEBOX_TYPE_MONITOR,
+                                  G_SIGNAL_RUN_FIRST,
+                                  0, NULL, NULL,
+                                  g_cclosure_marshal_VOID__STRING,
+                                  G_TYPE_NONE,
+                                  1, G_TYPE_STRING);
+
+}
+
+static void
+freebox_monitor_init (FreeboxMonitor *self)
+{
+  FreeboxMonitorPrivate *priv;
+  int error;
+
+  priv = self->priv = GET_PRIVATE (self);
+
+  priv->poll = avahi_glib_poll_new (NULL, G_PRIORITY_DEFAULT);
+  priv->client = avahi_client_new (avahi_glib_poll_get (priv->poll),
+                                   AVAHI_CLIENT_NO_FAIL,
+                                   on_client_state_changed,
+                                   self,
+                                   &error);
+}
+
+FreeboxMonitor *
+freebox_monitor_new (void)
+{
+  return g_object_new (FREEBOX_TYPE_MONITOR, NULL);
+}
diff --git a/src/freebox/freebox-monitor.h b/src/freebox/freebox-monitor.h
new file mode 100644
index 0000000..e2bb861
--- /dev/null
+++ b/src/freebox/freebox-monitor.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2011 Intel Corp
+ * Copyright (C) 2013 Bastien Nocera <hadess hadess net>
+ *
+ * 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; either version 2 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 package; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __FREEBOX_MONITOR_H__
+#define __FREEBOX_MONITOR_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define FREEBOX_TYPE_MONITOR (freebox_monitor_get_type())
+#define FREEBOX_MONITOR(obj)                                                 \
+   (G_TYPE_CHECK_INSTANCE_CAST ((obj),                                  \
+                                FREEBOX_TYPE_MONITOR,                        \
+                                FreeboxMonitor))
+#define FREEBOX_MONITOR_CLASS(klass)                                         \
+   (G_TYPE_CHECK_CLASS_CAST ((klass),                                   \
+                             FREEBOX_TYPE_MONITOR,                           \
+                             FreeboxMonitorClass))
+#define IS_FREEBOX_MONITOR(obj)                                              \
+   (G_TYPE_CHECK_INSTANCE_TYPE ((obj),                                  \
+                                FREEBOX_TYPE_MONITOR))
+#define IS_FREEBOX_MONITOR_CLASS(klass)                                      \
+   (G_TYPE_CHECK_CLASS_TYPE ((klass),                                   \
+                             FREEBOX_TYPE_MONITOR))
+#define FREEBOX_MONITOR_GET_CLASS(obj)                                       \
+   (G_TYPE_INSTANCE_GET_CLASS ((obj),                                   \
+                               FREEBOX_TYPE_MONITOR,                         \
+                               FreeboxMonitorClass))
+
+typedef struct _FreeboxMonitorPrivate FreeboxMonitorPrivate;
+typedef struct _FreeboxMonitor      FreeboxMonitor;
+typedef struct _FreeboxMonitorClass FreeboxMonitorClass;
+
+struct _FreeboxMonitor {
+  GObject parent;
+
+  FreeboxMonitorPrivate *priv;
+};
+
+struct _FreeboxMonitorClass {
+  GObjectClass parent_class;
+};
+
+GType freebox_monitor_get_type (void) G_GNUC_CONST;
+
+FreeboxMonitor *freebox_monitor_new (void);
+
+G_END_DECLS
+
+#endif /* __FREEBOX_MONITOR_H__ */
diff --git a/src/freebox/freebox.gresource.xml b/src/freebox/freebox.gresource.xml
new file mode 100644
index 0000000..5942941
--- /dev/null
+++ b/src/freebox/freebox.gresource.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gnome/grilo/plugins/freebox">
+    <file compressed="false">free.png</file>
+  </gresource>
+</gresources>
diff --git a/src/freebox/grl-freebox.c b/src/freebox/grl-freebox.c
new file mode 100644
index 0000000..7d53872
--- /dev/null
+++ b/src/freebox/grl-freebox.c
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2013 Bastien Nocera
+ *
+ * Contact: Bastien Nocera <hadess hadess net>
+ *
+ * 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 <gio/gio.h>
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include <stdlib.h>
+#include <pls/grl-pls.h>
+
+#include "grl-freebox.h"
+#include "freebox-monitor.h"
+
+#define FREEBOX_URL "http://mafreebox.freebox.fr/freeboxtv/playlist.m3u";
+
+/* --------- Logging  -------- */
+
+#define GRL_LOG_DOMAIN_DEFAULT freebox_log_domain
+GRL_LOG_DOMAIN_STATIC(freebox_log_domain);
+
+/* --- Plugin information --- */
+
+#define PLUGIN_ID   FREEBOX_PLUGIN_ID
+
+#define SOURCE_ID   "grl-freebox"
+#define SOURCE_NAME _("Freebox TV")
+#define SOURCE_DESC _("A source for browsing Freebox TV channels")
+
+/* --- Grilo Freebox Private --- */
+
+#define GRL_FREEBOX_SOURCE_GET_PRIVATE(object)           \
+  (G_TYPE_INSTANCE_GET_PRIVATE((object),                       \
+                               GRL_FREEBOX_SOURCE_TYPE,  \
+                               GrlFreeboxSourcePrivate))
+
+struct _GrlFreeboxSourcePrivate {
+  GrlMedia *media;
+  int       last_seen_channel;
+};
+
+/* --- Data types --- */
+
+static GrlFreeboxSource *grl_freebox_source_new (void);
+
+static void grl_freebox_source_finalize (GObject *object);
+
+gboolean grl_freebox_plugin_init (GrlRegistry *registry,
+                                  GrlPlugin   *plugin,
+                                  GList       *configs);
+
+static const GList *grl_freebox_source_supported_keys (GrlSource *source);
+
+static void grl_freebox_source_browse (GrlSource           *source,
+                                       GrlSourceBrowseSpec *bs);
+
+/* =================== Freebox Plugin  =============== */
+
+static void
+freebox_found (FreeboxMonitor *mon,
+               const char     *name,
+               GrlPlugin      *plugin)
+{
+  GrlFreeboxSource *source;
+  GrlRegistry *registry;
+
+  if (g_object_get_data (G_OBJECT (plugin), "source") != NULL)
+    return;
+
+  GRL_DEBUG ("Found a Freebox: %s", name);
+
+  source = grl_freebox_source_new ();
+  registry = grl_registry_get_default ();
+
+  g_object_set_data (G_OBJECT (plugin), "source", source);
+  grl_registry_register_source (registry,
+                                plugin,
+                                GRL_SOURCE (source),
+                                NULL);
+}
+
+static void
+freebox_lost (FreeboxMonitor *mon,
+              const char     *name,
+              GrlPlugin      *plugin)
+{
+  GrlFreeboxSource *source;
+  GrlRegistry *registry;
+
+  source = g_object_get_data (G_OBJECT (plugin), "source");
+  if (source == NULL)
+    return;
+
+  GRL_DEBUG ("Remove a Freebox: %s", name);
+
+  registry = grl_registry_get_default ();
+
+  grl_registry_unregister_source (registry,
+                                  GRL_SOURCE (source),
+                                  NULL);
+}
+
+gboolean
+grl_freebox_plugin_init (GrlRegistry *registry,
+                         GrlPlugin   *plugin,
+                         GList       *configs)
+{
+  FreeboxMonitor *mon;
+
+  GRL_LOG_DOMAIN_INIT (freebox_log_domain, "freebox");
+
+  GRL_DEBUG ("%s", __FUNCTION__);
+
+  /* Initialize i18n */
+  bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+
+  mon = freebox_monitor_new ();
+  g_signal_connect (mon, "found",
+                    G_CALLBACK (freebox_found), plugin);
+  g_signal_connect (mon, "lost",
+                    G_CALLBACK (freebox_lost), plugin);
+
+  return TRUE;
+}
+
+GRL_PLUGIN_REGISTER (grl_freebox_plugin_init,
+                     NULL,
+                     PLUGIN_ID);
+
+/* ================== Freebox GObject ================ */
+
+
+G_DEFINE_TYPE (GrlFreeboxSource,
+               grl_freebox_source,
+               GRL_TYPE_SOURCE);
+
+static GrlFreeboxSource *
+grl_freebox_source_new (void)
+{
+  GIcon *icon;
+  GFile *file;
+  GrlFreeboxSource *object;
+
+  GRL_DEBUG ("%s", __FUNCTION__);
+
+  file = g_file_new_for_uri ("resource:///org/gnome/grilo/plugins/freebox/free.png");
+  icon = g_file_icon_new (file);
+  g_object_unref (file);
+  object = g_object_new (GRL_FREEBOX_SOURCE_TYPE,
+                         "source-id", SOURCE_ID,
+                         "source-name", SOURCE_NAME,
+                         "source-desc", SOURCE_DESC,
+                         "supported-media", GRL_MEDIA_TYPE_VIDEO,
+                         "source-icon", icon,
+                         NULL);
+  return object;
+}
+
+static void
+grl_freebox_source_class_init (GrlFreeboxSourceClass * klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GrlSourceClass *source_class = GRL_SOURCE_CLASS (klass);
+
+  object_class->finalize = grl_freebox_source_finalize;
+
+  source_class->supported_keys = grl_freebox_source_supported_keys;
+  source_class->browse = grl_freebox_source_browse;
+
+  g_type_class_add_private (klass, sizeof (GrlFreeboxSourcePrivate));
+}
+
+static void
+grl_freebox_source_init (GrlFreeboxSource *source)
+{
+  GrlFreeboxSourcePrivate *priv;
+
+  priv = source->priv = GRL_FREEBOX_SOURCE_GET_PRIVATE(source);
+
+  priv->media = grl_media_new ();
+  grl_media_set_url (priv->media, FREEBOX_URL);
+}
+
+static void
+grl_freebox_source_finalize (GObject *object)
+{
+  GrlFreeboxSource *source = GRL_FREEBOX_SOURCE (object);
+  GrlFreeboxSourcePrivate *priv = GRL_FREEBOX_SOURCE (source)->priv;
+
+  g_object_unref (priv->media);
+
+  G_OBJECT_CLASS (grl_freebox_source_parent_class)->finalize (object);
+}
+
+/* ================== API Implementation ================ */
+
+static const GList *
+grl_freebox_source_supported_keys (GrlSource *source)
+{
+  static GList *keys = NULL;
+  if (!keys) {
+    keys = grl_metadata_key_list_new (GRL_METADATA_KEY_ID,
+                                      GRL_METADATA_KEY_TITLE,
+                                      GRL_METADATA_KEY_URL,
+                                      GRL_METADATA_KEY_MIME,
+                                      NULL);
+  }
+  return keys;
+}
+
+static char *
+cleanup_title (const char *title)
+{
+  const char *flavours[] = {
+    " (auto)",
+    " (bas débit)",
+    " (standard)",
+    " (HD)"
+  };
+  guint i;
+  const char *s;
+
+  s = strstr (title, " - ") + strlen (" - ");
+  g_return_val_if_fail (s != NULL, NULL);
+
+  for (i = 0; i < G_N_ELEMENTS (flavours); i++) {
+    if (g_str_has_suffix (s, flavours[i]))
+      return g_strndup (s, strlen (s) - strlen (flavours[i]));
+  }
+  return g_strdup (s);
+}
+
+static char *
+remove_flavour (const char *url)
+{
+  char *s;
+
+  s = strstr (url, "&flavour=");
+  if (s == NULL)
+    return g_strdup (url);
+  return g_strndup (url, s - url);
+}
+
+static GrlMedia *
+filter_func (GrlSource   *source,
+             GrlMedia    *media,
+             gpointer     user_data)
+{
+  GrlFreeboxSourcePrivate *priv = GRL_FREEBOX_SOURCE (source)->priv;
+  GrlMedia *ret;
+  const gchar *title;
+  char *new_title;
+  char *url;
+  int channel_num;
+
+  title = grl_media_get_title (media);
+  if (title == NULL) {
+    g_object_unref (media);
+    return NULL;
+  }
+
+  /* Title are of the form:
+   * channel_num - Name (flavour)
+   * such as:
+   * 689 - Star gold (standard)
+   *
+   * And channel numbers are ever increasing */
+  channel_num = atoi (title);
+  if (channel_num == priv->last_seen_channel) {
+    /* Already processed this title */
+    g_object_unref (media);
+    return NULL;
+  }
+  /* if channel_num is lower than last_seen_channel,
+   * we're actually processing the playlist again,
+   * so don't skip it */
+  priv->last_seen_channel = channel_num;
+
+  new_title = cleanup_title (title);
+  url = remove_flavour (grl_media_get_url (media));
+
+  ret = grl_media_video_new ();
+  grl_media_set_url (ret, url);
+  grl_media_set_id (ret, url);
+  grl_media_set_title (ret, new_title);
+  g_free (new_title);
+  g_free (url);
+
+  g_object_unref (media);
+
+  return ret;
+}
+
+static void
+grl_freebox_source_browse (GrlSource           *source,
+                           GrlSourceBrowseSpec *bs)
+{
+  GrlFreeboxSourcePrivate *priv = GRL_FREEBOX_SOURCE (source)->priv;
+
+  grl_pls_browse (source,
+                  priv->media,
+                  bs->keys,
+                  bs->options,
+                  filter_func,
+                  bs->callback,
+                  bs->user_data);
+}
diff --git a/src/freebox/grl-freebox.h b/src/freebox/grl-freebox.h
new file mode 100644
index 0000000..6cd0de5
--- /dev/null
+++ b/src/freebox/grl-freebox.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2013 Bastien Nocera
+ *
+ * Contact: Bastien Nocera <hadess hadess net>
+ *
+ * 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_FREEBOX_SOURCE_H_
+#define _GRL_FREEBOX_SOURCE_H_
+
+#include <grilo.h>
+
+#define GRL_FREEBOX_SOURCE_TYPE           \
+  (grl_freebox_source_get_type ())
+
+#define GRL_FREEBOX_SOURCE(obj)                          \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj),                          \
+                               GRL_FREEBOX_SOURCE_TYPE,  \
+                               GrlFreeboxSource))
+
+#define GRL_IS_FREEBOX_SOURCE(obj)                       \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj),                          \
+                               GRL_FREEBOX_SOURCE_TYPE))
+
+#define GRL_FREEBOX_SOURCE_CLASS(klass)               \
+  (G_TYPE_CHECK_CLASS_CAST((klass),                         \
+                           GRL_FREEBOX_SOURCE_TYPE,   \
+                           GrlFreeboxSourceClass))
+
+#define GRL_IS_FREEBOX_SOURCE_CLASS(klass)            \
+  (G_TYPE_CHECK_CLASS_TYPE((klass)                          \
+                           GRL_FREEBOX_SOURCE_TYPE))
+
+#define GRL_FREEBOX_SOURCE_GET_CLASS(obj)                \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj),                           \
+                              GRL_FREEBOX_SOURCE_TYPE,   \
+                              GrlFreeboxSourceClass))
+
+
+typedef struct _GrlFreeboxSource GrlFreeboxSource;
+typedef struct _GrlFreeboxSourcePrivate GrlFreeboxSourcePrivate;
+
+struct _GrlFreeboxSource {
+
+  GrlSource parent;
+
+  /*< private >*/
+  GrlFreeboxSourcePrivate *priv;
+};
+
+typedef struct _GrlFreeboxSourceClass GrlFreeboxSourceClass;
+
+struct _GrlFreeboxSourceClass {
+
+  GrlSourceClass parent_class;
+
+};
+
+GType grl_freebox_source_get_type (void);
+
+#endif /* _GRL_FREEBOX_SOURCE_H_ */
diff --git a/src/freebox/grl-freebox.xml b/src/freebox/grl-freebox.xml
new file mode 100644
index 0000000..41baf10
--- /dev/null
+++ b/src/freebox/grl-freebox.xml
@@ -0,0 +1,10 @@
+<plugin>
+  <info>
+    <name>Freebox</name>
+    <module>libgrlfreebox</module>
+    <description>A plugin for Freebox TV channels</description>
+    <author>GNOME</author>
+    <license>LGPL</license>
+    <site>http://www.gnome.org</site>
+  </info>
+</plugin>


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