[grilo-plugins] opensubtitles: Add plugin



commit 5a968507e610f35888d01fc919c104b3940cf3f3
Author: Bastien Nocera <hadess hadess net>
Date:   Fri Nov 28 11:12:51 2014 +0100

    opensubtitles: Add plugin
    
    Add a plugin that will get a list of available subtitles available
    for a video, given its gibest hash.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=740871

 configure.ac                            |   45 +++
 src/Makefile.am                         |    8 +-
 src/opensubtitles/Makefile.am           |   39 ++
 src/opensubtitles/grl-opensubtitles.c   |  630 +++++++++++++++++++++++++++++++
 src/opensubtitles/grl-opensubtitles.h   |   74 ++++
 src/opensubtitles/grl-opensubtitles.xml |   10 +
 6 files changed, 804 insertions(+), 2 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index dc087a0..414872c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1348,6 +1348,50 @@ DEPS_FREEBOX_LIBS="$DEPS_LIBS $GRLPLS_LIBS $AVAHI_LIBS"
 AC_SUBST(DEPS_FREEBOX_LIBS)
 
 # ----------------------------------------------------------
+# BUILD OPENSUBTITLES PLUGIN
+# ----------------------------------------------------------
+
+AC_ARG_ENABLE(opensubtitles,
+        AC_HELP_STRING([--enable-opensubtitles],
+                [enable DLNA plugin using the OpenSubtitles DBus services (default: yes)]),
+                [
+                  case "$enableval" in
+                     yes)
+                        if test "x$HAVE_GIO" = "xno"; then
+                           AC_MSG_ERROR([GIO not found, install it or use --disable-opensubtitles])
+                        fi
+                        if test "$xHAVE_LIBSOUP" = "xno"; then
+                           AC_MSG_ERROR([libsoup-2.4 not found, install it or use --disable-opensubtitles])
+                        fi
+                        ;;
+                  esac
+
+                ],
+                [
+                  if test "x$HAVE_GIO" = "xyes" -a "x$HAVE_LIBSOUP" = "xyes"; then
+                     enable_opensubtitles=yes
+                  else
+                     enable_opensubtitles=no
+                  fi
+                ])
+
+AM_CONDITIONAL([OPENSUBTITLES_PLUGIN], [test "x$enable_opensubtitles" = "xyes"])
+GRL_PLUGINS_ALL="$GRL_PLUGINS_ALL opensubtitles"
+if test "x$enable_opensubtitles" = "xyes"
+then
+       GRL_PLUGINS_ENABLED="$GRL_PLUGINS_ENABLED opensubtitles"
+fi
+
+OPENSUBTITLES_PLUGIN_ID="grl-opensubtitles"
+AC_SUBST(OPENSUBTITLES_PLUGIN_ID)
+AC_DEFINE_UNQUOTED([OPENSUBTITLES_PLUGIN_ID], ["$OPENSUBTITLES_PLUGIN_ID"], [OpenSubtitles plugin ID])
+
+DEPS_OPENSUBTITLES_CFLAGS="$DEPS_CFLAGS $GIO_CFLAGS $LIBSOUP_CFLAGS"
+AC_SUBST(DEPS_OPENSUBTITLES_CFLAGS)
+DEPS_OPENSUBTITLES_LIBS="$DEPS_LIBS $GIO_LIBS $LIBSOUP_LIBS"
+AC_SUBST(DEPS_OPENSUBTITLES_LIBS)
+
+# ----------------------------------------------------------
 # GETTEXT/INTLTOOL
 # ----------------------------------------------------------
 
@@ -1399,6 +1443,7 @@ AC_CONFIG_FILES([
   src/magnatune/Makefile
   src/thetvdb/Makefile
   src/metadata-store/Makefile
+  src/opensubtitles/Makefile
   src/optical-media/Makefile
   src/pocket/Makefile
   src/podcasts/Makefile
diff --git a/src/Makefile.am b/src/Makefile.am
index 368b965..67be649 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -110,10 +110,14 @@ if FREEBOX_PLUGIN
 SUBDIRS += freebox
 endif
 
+if OPENSUBTITLES_PLUGIN
+SUBDIRS += opensubtitles
+endif
+
 DIST_SUBDIRS = \
    apple-trailers bliptv bookmarks dleyna dmap filesystem flickr freebox gravatar jamendo \
-   lastfm-albumart local-metadata lua-factory magnatune metadata-store optical-media   \
-   pocket podcasts raitv shoutcast thetvdb tmdb tracker vimeo youtube
+   lastfm-albumart local-metadata lua-factory magnatune metadata-store opensubtitles \
+   optical-media pocket podcasts raitv shoutcast thetvdb tmdb tracker vimeo youtube
 
 MAINTAINERCLEANFILES = \
         *.in           \
diff --git a/src/opensubtitles/Makefile.am b/src/opensubtitles/Makefile.am
new file mode 100644
index 0000000..a3a9fbd
--- /dev/null
+++ b/src/opensubtitles/Makefile.am
@@ -0,0 +1,39 @@
+include $(top_srcdir)/gtester.mk
+
+ext_LTLIBRARIES                 = libgrlopensubtitles.la
+
+libgrlopensubtitles_la_CFLAGS =        \
+       $(DEPS_OPENSUBTITLES_CFLAGS)    \
+       -DG_LOG_DOMAIN=\"GrlOpenSubtitles\" \
+       -DLOCALEDIR=\"$(localedir)\"
+
+
+libgrlopensubtitles_la_LIBADD =        \
+       $(DEPS_OPENSUBTITLES_LIBS)
+
+libgrlopensubtitles_la_LDFLAGS = \
+       -no-undefined            \
+       -module                  \
+       -avoid-version
+
+libgrlopensubtitles_la_SOURCES = grl-opensubtitles.c grl-opensubtitles.h
+
+extdir                 = $(GRL_PLUGINS_DIR)
+opensubtitlesxmldir    = $(GRL_PLUGINS_DIR)
+opensubtitlesxml_DATA  = $(OPENSUBTITLES_PLUGIN_ID).xml
+
+# This lets us test the plugin without installing it,
+# because grilo expects the .so and .xml files to be in
+# the same directory:
+copy-xml-to-libs-dir: libgrlopensubtitles.la
+       cp -f $(srcdir)/$(opensubtitlesxml_DATA) $(builddir)/.libs/
+
+all-local: copy-xml-to-libs-dir
+
+EXTRA_DIST += $(opensubtitlesxml_DATA)
+
+MAINTAINERCLEANFILES    = \
+       *.in \
+       *~
+
+DISTCLEANFILES = $(MAINTAINERCLEANFILES)
diff --git a/src/opensubtitles/grl-opensubtitles.c b/src/opensubtitles/grl-opensubtitles.c
new file mode 100644
index 0000000..107ab4b
--- /dev/null
+++ b/src/opensubtitles/grl-opensubtitles.c
@@ -0,0 +1,630 @@
+/*
+ * Copyright (C) 2014 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
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+
+#include <grilo.h>
+#include <gio/gio.h>
+#include <glib/gi18n-lib.h>
+#include <libsoup/soup.h>
+
+#include "grl-opensubtitles.h"
+
+#define GRL_LOG_DOMAIN_DEFAULT opensubtitles_log_domain
+GRL_LOG_DOMAIN_STATIC(opensubtitles_log_domain);
+
+#define PLUGIN_ID   OPENSUBTITLES_PLUGIN_ID
+
+#define SOURCE_ID   "grl-opensubtitles"
+#define SOURCE_NAME _("OpenSubtitles Provider")
+#define SOURCE_DESC _("A source providing a list of subtitles for a video")
+
+#define GRL_OPENSUBTITLES_SOURCE_GET_PRIVATE(object)           \
+  (G_TYPE_INSTANCE_GET_PRIVATE((object),                        \
+                               GRL_OPENSUBTITLES_SOURCE_TYPE,  \
+                               GrlOpenSubtitlesSourcePriv))
+
+struct _GrlOpenSubtitlesSourcePriv {
+  char *token;
+  gboolean token_requested;
+
+  GrlKeyID hash_keyid;
+  SoupSession *session;
+  GAsyncQueue *queue;
+};
+
+typedef struct {
+  char *url;
+  guint downloads;
+  guint score;
+} SubtitleData;
+
+static GrlKeyID GRL_OPENSUBTITLES_METADATA_KEY_SUBTITLES_URL = GRL_METADATA_KEY_INVALID;
+static GrlKeyID GRL_OPENSUBTITLES_METADATA_KEY_SUBTITLES_LANG = GRL_METADATA_KEY_INVALID;
+
+/**/
+
+static GrlOpenSubtitlesSource *grl_opensubtitles_source_new (void);
+
+static void grl_opensubtitles_source_resolve (GrlSource            *source,
+                                              GrlSourceResolveSpec *rs);
+
+static const GList *grl_opensubtitles_source_supported_keys (GrlSource *source);
+
+static void grl_opensubtitles_source_cancel (GrlSource *source,
+                                             guint      operation_id);
+
+static gboolean grl_opensubtitles_source_may_resolve (GrlSource  *source,
+                                                      GrlMedia   *media,
+                                                      GrlKeyID    key_id,
+                                                      GList     **missing_keys);
+
+gboolean grl_opensubtitles_source_plugin_init (GrlRegistry *registry,
+                                               GrlPlugin   *plugin,
+                                               GList       *configs);
+
+/* =================== GrlOpenSubtitles Plugin  =============== */
+
+static GrlKeyID
+register_metadata_key (GrlRegistry *registry,
+                       const char *name,
+                       const char *nick,
+                       const char *blurb)
+{
+  GParamSpec *spec;
+  GrlKeyID key;
+
+  spec = g_param_spec_string (name,
+                              nick,
+                              blurb,
+                              NULL,
+                              G_PARAM_READWRITE
+                              | G_PARAM_STATIC_STRINGS);
+
+  key = grl_registry_register_metadata_key (registry, spec, NULL);
+
+  if (key == GRL_METADATA_KEY_INVALID) {
+    key = grl_registry_lookup_metadata_key (registry, name);
+    if (grl_metadata_key_get_type (key) != G_TYPE_STRING) {
+      key = GRL_METADATA_KEY_INVALID;
+    }
+  }
+
+  return key;
+}
+
+gboolean
+grl_opensubtitles_source_plugin_init (GrlRegistry *registry,
+                                      GrlPlugin   *plugin,
+                                      GList       *configs)
+{
+  GRL_LOG_DOMAIN_INIT (opensubtitles_log_domain, "opensubtitles");
+
+  GRL_DEBUG ("grl_opensubtitles_source_plugin_init");
+
+  /* Initialize i18n */
+  bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+
+  GRL_OPENSUBTITLES_METADATA_KEY_SUBTITLES_URL =
+    register_metadata_key (registry,
+                           "subtitles-url",
+                           "subtitles-url",
+                           "Subtitles URL");
+
+  GRL_OPENSUBTITLES_METADATA_KEY_SUBTITLES_LANG =
+    register_metadata_key (registry,
+                           "subtitles-lang",
+                           "subtitles-lang",
+                           "Subtitles Language");
+
+  GrlOpenSubtitlesSource *source = grl_opensubtitles_source_new ();
+  grl_registry_register_source (registry,
+                                plugin,
+                                GRL_SOURCE (source),
+                                NULL);
+  return TRUE;
+}
+
+GRL_PLUGIN_REGISTER (grl_opensubtitles_source_plugin_init,
+                     NULL,
+                     PLUGIN_ID);
+
+/* ================== GrlOpenSubtitles GObject ================ */
+
+static GrlOpenSubtitlesSource *
+grl_opensubtitles_source_new (void)
+{
+  GRL_DEBUG ("grl_opensubtitles_source_new");
+  return g_object_new (GRL_OPENSUBTITLES_SOURCE_TYPE,
+                      "source-id", SOURCE_ID,
+                      "source-name", SOURCE_NAME,
+                      "source-desc", SOURCE_DESC,
+                      NULL);
+}
+
+static void
+grl_opensubtitles_source_class_init (GrlOpenSubtitlesSourceClass * klass)
+{
+  GrlSourceClass *source_class = GRL_SOURCE_CLASS (klass);
+
+  source_class->supported_keys = grl_opensubtitles_source_supported_keys;
+  source_class->cancel = grl_opensubtitles_source_cancel;
+  source_class->may_resolve = grl_opensubtitles_source_may_resolve;
+  source_class->resolve = grl_opensubtitles_source_resolve;
+
+  g_type_class_add_private (klass, sizeof (GrlOpenSubtitlesSourcePriv));
+}
+
+static void
+grl_opensubtitles_source_init (GrlOpenSubtitlesSource *source)
+{
+  GrlOpenSubtitlesSourcePriv *priv = GRL_OPENSUBTITLES_SOURCE_GET_PRIVATE (source);
+
+  priv->session = soup_session_new ();
+  priv->queue = g_async_queue_new ();
+}
+
+G_DEFINE_TYPE (GrlOpenSubtitlesSource,
+               grl_opensubtitles_source,
+               GRL_TYPE_SOURCE);
+
+/* ======================= Utilities ==================== */
+
+static void
+ensure_hash_keyid (GrlOpenSubtitlesSourcePriv *priv)
+{
+  if (priv->hash_keyid == GRL_METADATA_KEY_INVALID) {
+    GrlRegistry *registry = grl_registry_get_default ();
+    priv->hash_keyid = grl_registry_lookup_metadata_key (registry, "gibest-hash");
+  }
+}
+
+static SoupMessage *
+new_login_message (void)
+{
+  SoupMessage *msg;
+
+  msg = soup_xmlrpc_request_new ("http://api.opensubtitles.org/xml-rpc";,
+                                "LogIn",
+                                G_TYPE_STRING, "",
+                                G_TYPE_STRING, "",
+                                G_TYPE_STRING, "en",
+                                G_TYPE_STRING, "Totem",
+                                G_TYPE_INVALID);
+
+  return msg;
+}
+
+static SoupMessage *
+new_search_message (const char *token,
+                   const char *hash,
+                   gint64      size)
+{
+  SoupMessage *msg;
+  GValueArray *array;
+  GHashTable *fields;
+  char *size_str;
+  GValue sublanguageid = G_VALUE_INIT;
+  GValue moviehash = G_VALUE_INIT;
+  GValue moviebytesize = G_VALUE_INIT;
+  GValue fieldsval = G_VALUE_INIT;
+
+  size_str = g_strdup_printf ("%" G_GINT64_FORMAT, size);
+
+  fields = g_hash_table_new (g_str_hash, g_str_equal);
+  g_value_init (&sublanguageid, G_TYPE_STRING);
+  g_value_set_string (&sublanguageid, "all");
+  g_hash_table_insert (fields, "sublanguageid", &sublanguageid);
+  g_value_init (&moviehash, G_TYPE_STRING);
+  g_value_set_string (&moviehash, hash);
+  g_hash_table_insert (fields, "moviehash", &moviehash);
+  g_value_init (&moviebytesize, G_TYPE_STRING);
+  g_value_set_string (&moviebytesize, size_str);
+  g_hash_table_insert (fields, "moviebytesize", &moviebytesize);
+
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+
+  array = g_value_array_new (1);
+  g_value_init (&fieldsval, G_TYPE_HASH_TABLE);
+  g_value_set_boxed (&fieldsval, fields);
+  g_value_array_append (array, &fieldsval);
+
+  msg = soup_xmlrpc_request_new ("http://api.opensubtitles.org/xml-rpc";,
+                                "SearchSubtitles",
+                                G_TYPE_STRING, token,
+                                G_TYPE_VALUE_ARRAY, array,
+                                G_TYPE_INVALID);
+
+  g_value_array_free (array);
+  g_hash_table_unref (fields);
+
+G_GNUC_END_IGNORE_DEPRECATIONS
+
+  return msg;
+}
+
+static const char *
+lookup_string (GHashTable *ht,
+              const char *key)
+{
+  GValue *val;
+  val = g_hash_table_lookup (ht, key);
+  if (!val)
+    return NULL;
+  return g_value_get_string (val);
+}
+
+static void
+subtitle_data_free (SubtitleData *sub)
+{
+  g_free (sub->url);
+  g_free (sub);
+}
+
+static int
+lookup_int (GHashTable *ht,
+           const char *key)
+{
+  GValue *val;
+  val = g_hash_table_lookup (ht, key);
+  if (!val)
+    return 0;
+  return atoi (g_value_get_string (val));
+}
+
+static void
+subs_foreach (gpointer key,
+             gpointer value,
+             gpointer user_data)
+{
+  const char *lang = key;
+  SubtitleData *sub = value;
+  GrlMedia *media = user_data;
+  GrlRelatedKeys *relkeys;
+
+  relkeys = grl_related_keys_new ();
+  grl_related_keys_set_string (relkeys,
+                              GRL_OPENSUBTITLES_METADATA_KEY_SUBTITLES_LANG,
+                              lang);
+  grl_related_keys_set_string (relkeys,
+                              GRL_OPENSUBTITLES_METADATA_KEY_SUBTITLES_URL,
+                              sub->url);
+  grl_data_add_related_keys (GRL_DATA (media), relkeys);
+}
+
+static void
+maybe_add_sub (GHashTable   *subs,
+              const char   *lang,
+              SubtitleData *sub)
+{
+  SubtitleData *old;
+
+  old = g_hash_table_lookup (subs, lang);
+  if (!old) {
+    g_hash_table_insert (subs, g_strdup (lang), sub);
+    return;
+  }
+
+  if (sub->score > old->score ||
+      (sub->score == old->score && sub->downloads > old->downloads)) {
+    g_hash_table_insert (subs, g_strdup (lang), sub);
+    return;
+  }
+
+  subtitle_data_free (sub);
+}
+
+static char *
+fixup_sub_url (const char *url)
+{
+  GString *str;
+
+  if (!g_str_has_suffix (url, ".gz"))
+    return g_strdup (url);
+
+  str = g_string_new (NULL);
+  g_string_insert_len (str, -1, url, strlen (url) - strlen (".gz"));
+  g_string_insert (str, -1, ".srt");
+  return g_string_free (str, FALSE);
+}
+
+static void
+parse_results (GrlMedia    *media,
+              SoupMessage *msg)
+{
+  SoupBuffer *body;
+  GError *error = NULL;
+  GHashTable *response;
+  GValue *data_val;
+  GValueArray *data;
+  guint i;
+  GHashTable *subs;
+
+  body = soup_message_body_flatten (msg->response_body);
+  if (!soup_xmlrpc_extract_method_response (body->data, body->length, &error,
+                                           G_TYPE_HASH_TABLE, &response)) {
+    GRL_WARNING ("Parsing search response failed: %s", error->message);
+    g_error_free (error);
+    soup_buffer_free (body);
+    return;
+  }
+
+  data_val = g_hash_table_lookup (response, "data");
+  if (!data_val)
+    goto out;
+  data = g_value_get_boxed (data_val);
+  subs = g_hash_table_new_full (g_str_hash, g_str_equal,
+                               g_free, (GDestroyNotify) subtitle_data_free);
+
+  for (i = 0; i < data->n_values; i++) {
+    GValue *val;
+    GHashTable *ht;
+    const char *lang;
+    SubtitleData *sub;
+
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+
+    val = g_value_array_get_nth (data, i);
+    ht = g_value_get_boxed (val);
+
+    /* Verify that the season/episode matches the media
+     * before using that data */
+    if (grl_data_has_key (GRL_DATA (media), GRL_METADATA_KEY_SHOW)) {
+      int episode, season;
+
+      episode = lookup_int (ht, "SeriesSeason");
+      season = lookup_int (ht, "SeriesEpisode");
+      if (season != grl_media_video_get_episode (GRL_MEDIA_VIDEO (media)) ||
+          episode != grl_media_video_get_season (GRL_MEDIA_VIDEO (media))) {
+        continue;
+      }
+    }
+
+    lang = lookup_string (ht, "ISO639");
+
+    sub = g_new0 (SubtitleData, 1);
+    sub->url = fixup_sub_url (lookup_string (ht, "SubDownloadLink"));
+    sub->downloads = lookup_int (ht, "SubDownloadsCnt");
+    sub->score = 0;
+
+    /* Scoring system from popcorn-opensubtitles */
+    if (g_strcmp0 (lookup_string (ht, "MatchedBy"), "moviehash") == 0)
+      sub->score += 100;
+    if (g_strcmp0 (lookup_string (ht, "MatchedBy"), "tag") == 0)
+      sub->score += 50;
+    if (g_strcmp0 (lookup_string (ht, "UserRank"), "trusted") == 0)
+      sub->score += 100;
+
+    maybe_add_sub (subs, lang, sub);
+
+G_GNUC_END_IGNORE_DEPRECATIONS
+  }
+
+  g_hash_table_foreach (subs, subs_foreach, media);
+  g_hash_table_unref (subs);
+
+out:
+  g_hash_table_unref (response);
+  soup_buffer_free (body);
+}
+
+static void
+search_done_cb (SoupSession *session,
+               SoupMessage *msg,
+               gpointer     user_data)
+{
+  GrlSourceResolveSpec *rs = user_data;
+
+  if (msg->status_code != 200) {
+    GError *error = NULL;
+
+    GRL_DEBUG ("Failed to login: HTTP code %d", msg->status_code);
+    error = g_error_new (GRL_CORE_ERROR,
+                         GRL_CORE_ERROR_RESOLVE_FAILED,
+                         "Failed to login to OpenSubtitles.org (HTTP code %d)",
+                         msg->status_code);
+    rs->callback (rs->source, rs->operation_id, rs->media, rs->user_data, error);
+    g_clear_error (&error);
+  } else {
+    parse_results (rs->media, msg);
+    rs->callback (rs->source, rs->operation_id, rs->media, rs->user_data, NULL);
+  }
+}
+
+static char *
+parse_token (SoupMessage *msg)
+{
+  SoupBuffer *body;
+  char *token = NULL;
+  GHashTable *ht;
+  GError *error = NULL;
+
+  body = soup_message_body_flatten (msg->response_body);
+  if (!soup_xmlrpc_extract_method_response (body->data, body->length, &error,
+                                            G_TYPE_HASH_TABLE, &ht)) {
+    GRL_WARNING ("Parsing token response failed: %s", error->message);
+    g_error_free (error);
+    soup_buffer_free (body);
+    return NULL;
+  }
+  token = g_value_dup_string (g_hash_table_lookup (ht, "token"));
+  g_hash_table_unref (ht);
+  soup_buffer_free (body);
+  return token;
+}
+
+static void
+login_done_cb (SoupSession *session,
+              SoupMessage *msg,
+              gpointer     user_data)
+{
+  GrlOpenSubtitlesSourcePriv *priv =
+    GRL_OPENSUBTITLES_SOURCE_GET_PRIVATE (user_data);
+  gboolean failed = FALSE;
+  GrlSourceResolveSpec *rs;
+  GError *error = NULL;
+
+  if (msg->status_code != 200) {
+    GRL_DEBUG ("Failed to login: HTTP code %d", msg->status_code);
+    failed = TRUE;
+  } else {
+    priv->token = parse_token (msg);
+    if (!priv->token) {
+      failed = TRUE;
+      msg->status_code = 666;
+    }
+  }
+
+  if (failed) {
+    error = g_error_new (GRL_CORE_ERROR,
+                         GRL_CORE_ERROR_RESOLVE_FAILED,
+                         "Failed to fetch subtitles from OpenSubtitles.org (HTTP code %d)",
+                         msg->status_code);
+  }
+
+  while ((rs = g_async_queue_try_pop (priv->queue))) {
+    if (failed) {
+      rs->callback (rs->source, rs->operation_id, rs->media, rs->user_data, error);
+    } else {
+       SoupMessage *search;
+       search = new_search_message (priv->token,
+                                    grl_data_get_string (GRL_DATA (rs->media), priv->hash_keyid),
+                                    grl_media_get_size (rs->media));
+       grl_operation_set_data (rs->operation_id, search);
+       soup_session_queue_message (session, search, search_done_cb, rs);
+    }
+  }
+
+  g_clear_error (&error);
+}
+
+/* ================== API Implementation ================ */
+
+static const GList *
+grl_opensubtitles_source_supported_keys (GrlSource *source)
+{
+  static GList *keys = NULL;
+  if (!keys) {
+    keys = grl_metadata_key_list_new (GRL_OPENSUBTITLES_METADATA_KEY_SUBTITLES_URL,
+                                      GRL_OPENSUBTITLES_METADATA_KEY_SUBTITLES_LANG,
+                                      NULL);
+  }
+  return keys;
+}
+
+static gboolean
+grl_opensubtitles_source_may_resolve (GrlSource *source,
+                                      GrlMedia  *media,
+                                      GrlKeyID   key_id,
+                                      GList    **missing_keys)
+{
+  GrlOpenSubtitlesSourcePriv *priv =
+    GRL_OPENSUBTITLES_SOURCE_GET_PRIVATE (source);
+
+  ensure_hash_keyid (priv);
+  if (priv->hash_keyid == GRL_METADATA_KEY_INVALID)
+    return FALSE;
+
+  if (!media ||
+      !grl_data_has_key (GRL_DATA (media), priv->hash_keyid) ||
+      !grl_data_has_key (GRL_DATA (media), GRL_METADATA_KEY_SIZE)) {
+    if (missing_keys) {
+      GList *keys = NULL;
+      if (!grl_data_has_key (GRL_DATA (media), priv->hash_keyid))
+        keys = g_list_prepend (keys, GRLKEYID_TO_POINTER (priv->hash_keyid));
+      if (!grl_data_has_key (GRL_DATA (media), GRL_METADATA_KEY_SIZE))
+        keys = g_list_prepend (keys, GRLKEYID_TO_POINTER (GRL_METADATA_KEY_SIZE));
+      *missing_keys = keys;
+    }
+    return FALSE;
+  }
+
+  if (!GRL_IS_MEDIA_VIDEO (media))
+    return FALSE;
+
+  if (grl_data_has_key (GRL_DATA (media), GRL_METADATA_KEY_SHOW)) {
+    gboolean has_episode, has_season;
+
+    has_episode = grl_data_has_key (GRL_DATA (media), GRL_METADATA_KEY_EPISODE);
+    has_season = grl_data_has_key (GRL_DATA (media), GRL_METADATA_KEY_SEASON);
+
+    if (!has_episode || !has_season) {
+      if (missing_keys) {
+        GList *keys = NULL;
+        if (!has_episode)
+          keys = g_list_prepend (keys, GRLKEYID_TO_POINTER(GRL_METADATA_KEY_EPISODE));
+        if (!has_season)
+          keys = g_list_prepend (keys, GRLKEYID_TO_POINTER(GRL_METADATA_KEY_SEASON));
+        *missing_keys = keys;
+      }
+      return FALSE;
+    }
+  }
+
+  return TRUE;
+}
+
+static void
+grl_opensubtitles_source_resolve (GrlSource            *source,
+                                  GrlSourceResolveSpec *rs)
+{
+  GrlOpenSubtitlesSourcePriv *priv =
+    GRL_OPENSUBTITLES_SOURCE_GET_PRIVATE (source);
+  SoupMessage *msg;
+
+  GRL_DEBUG (__FUNCTION__);
+
+  ensure_hash_keyid (priv);
+
+  if (!priv->token) {
+    if (!priv->token_requested) {
+      msg = new_login_message ();
+      grl_operation_set_data (rs->operation_id, msg);
+      soup_session_queue_message (priv->session, msg, login_done_cb, source);
+    }
+    g_async_queue_push (priv->queue, rs);
+    return;
+  }
+
+  msg = new_search_message (priv->token,
+                            grl_data_get_string (GRL_DATA (rs->media), priv->hash_keyid),
+                            grl_media_get_size (rs->media));
+  grl_operation_set_data (rs->operation_id, msg);
+  soup_session_queue_message (priv->session, msg, search_done_cb, rs);
+}
+
+static void
+grl_opensubtitles_source_cancel (GrlSource *source,
+                                 guint operation_id)
+{
+  GrlOpenSubtitlesSourcePriv *priv =
+    GRL_OPENSUBTITLES_SOURCE_GET_PRIVATE (source);
+  SoupMessage *msg = grl_operation_get_data (operation_id);
+
+  if (msg)
+    soup_session_cancel_message (priv->session, msg, SOUP_STATUS_CANCELLED);
+}
diff --git a/src/opensubtitles/grl-opensubtitles.h b/src/opensubtitles/grl-opensubtitles.h
new file mode 100644
index 0000000..22f8210
--- /dev/null
+++ b/src/opensubtitles/grl-opensubtitles.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014 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_OPENSUBTITLES_SOURCE_H_
+#define _GRL_OPENSUBTITLES_SOURCE_H_
+
+#include <grilo.h>
+
+#define GRL_OPENSUBTITLES_SOURCE_TYPE           \
+  (grl_opensubtitles_source_get_type ())
+
+#define GRL_OPENSUBTITLES_SOURCE(obj)                           \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj),                           \
+                               GRL_OPENSUBTITLES_SOURCE_TYPE,   \
+                               GrlOpenSubtitlesSource))
+
+#define GRL_IS_OPENSUBTITLES_SOURCE(obj)                        \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj),                           \
+                               GRL_OPENSUBTITLES_SOURCE_TYPE))
+
+#define GRL_OPENSUBTITLES_SOURCE_CLASS(klass)                   \
+  (G_TYPE_CHECK_CLASS_CAST((klass),                             \
+                           GRL_OPENSUBTITLES_SOURCE_TYPE,       \
+                           GrlOpenSubtitlesSourceClass))
+
+#define GRL_IS_OPENSUBTITLES_SOURCE_CLASS(klass)                \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),                             \
+                           GRL_OPENSUBTITLES_SOURCE_TYPE))
+
+#define GRL_OPENSUBTITLES_SOURCE_GET_CLASS(obj)                 \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj),                            \
+                              GRL_OPENSUBTITLES_SOURCE_TYPE,    \
+                              GrlOpenSubtitlesSourceClass))
+
+typedef struct _GrlOpenSubtitlesSource     GrlOpenSubtitlesSource;
+typedef struct _GrlOpenSubtitlesSourcePriv GrlOpenSubtitlesSourcePriv;
+
+struct _GrlOpenSubtitlesSource {
+
+  GrlSource parent;
+
+  /*< private >*/
+  GrlOpenSubtitlesSourcePriv *priv;
+
+};
+
+typedef struct _GrlOpenSubtitlesSourceClass GrlOpenSubtitlesSourceClass;
+
+struct _GrlOpenSubtitlesSourceClass {
+
+  GrlSourceClass parent_class;
+
+};
+
+GType grl_opensubtitles_source_get_type (void);
+
+#endif /* _GRL_OPENSUBTITLES_SOURCE_H_ */
diff --git a/src/opensubtitles/grl-opensubtitles.xml b/src/opensubtitles/grl-opensubtitles.xml
new file mode 100644
index 0000000..0d99d3f
--- /dev/null
+++ b/src/opensubtitles/grl-opensubtitles.xml
@@ -0,0 +1,10 @@
+<plugin>
+  <info>
+    <name>OpenSubtitles Provider</name>
+    <module>libgrlopensubtitles</module>
+    <description>A plugin that gets a list of subtitles for a video</description>
+    <author>Bastien Nocera</author>
+    <license>LGPL</license>
+    <site>http://www.hadess.net</site>
+  </info>
+</plugin>


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