[discident-glib] Add EAN query support



commit 1546ded8f8c90e05f4f3d31ed93dead59ae43659
Author: Bastien Nocera <hadess hadess net>
Date:   Thu Dec 16 19:08:56 2010 +0000

    Add EAN query support
    
    Only sync functions right now.

 configure.ac                          |    6 +-
 discident-glib/Makefile.am            |   12 +-
 discident-glib/discident-ean-glib.c   |  361 +++++++++++++++++++++++++++++++++
 discident-glib/discident-ean-glib.h   |   61 ++++++
 discident-glib/discident-glib.symbols |    5 +
 discident-glib/test-diglib.c          |   28 ++-
 6 files changed, 462 insertions(+), 11 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index cc0f0b6..630765a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -51,10 +51,12 @@ GTK_DOC_CHECK(1.9)
 
 AC_DEFINE_UNQUOTED(LOCALEDIR, "${prefix}/share/locale", [Directory for the localization files])
 
-dnl Requires for the properties window
+dnl Requires for the library
 PKG_CHECK_MODULES(DISCIDENT,
 		  gio-2.0
-		  json-glib-1.0)
+		  json-glib-1.0
+		  libsoup-gnome-2.4
+		  libxml-2.0)
 
 GLIB_GENMARSHAL=`$PKG_CONFIG --variable=glib_genmarshal glib-2.0`
 AC_SUBST(GLIB_GENMARSHAL)
diff --git a/discident-glib/Makefile.am b/discident-glib/Makefile.am
index 5ed739e..2ed8d08 100644
--- a/discident-glib/Makefile.am
+++ b/discident-glib/Makefile.am
@@ -7,7 +7,11 @@ lib_LTLIBRARIES = libdiscident-glib.la
 
 public_files =						\
 	discident-glib.c				\
-	discident-glib.h
+	discident-glib.h				\
+	discident-ean-glib.c				\
+	discident-ean-glib.h				\
+	discident-error.c				\
+	discident-error.h
 
 libdiscident_glib_la_SOURCES =				\
 	$(public_files)					\
@@ -22,7 +26,7 @@ libdiscident_glib_la_LDFLAGS =				\
 	-export-symbols $(srcdir)/discident-glib.symbols
 
 diglibdir = $(pkgincludedir)
-diglib_HEADERS = discident-glib.h
+diglib_HEADERS = discident-glib.h discident-ean-glib.h
 
 AM_CFLAGS = -I$(srcdir) $(DISCIDENT_CFLAGS) $(COMMON_CFLAGS) $(WARN_CFLAGS) $(DISABLE_DEPRECATED)
 
@@ -36,8 +40,8 @@ if HAVE_INTROSPECTION
 introspection_files = $(public_files)
 
 DiscidentGlib-1.0.gir: libdiscident-glib.la
-DiscidentGlib_1_0_gir_INCLUDES = GObject-2.0 Gio-2.0
-DiscidentGlib_1_0_gir_PACKAGES = gobject-2.0 gmodule-2.0 glib-2.0 gio-2.0
+DiscidentGlib_1_0_gir_INCLUDES = GObject-2.0 Gio-2.0 Json-1.0 SoupGNOME-2.4
+DiscidentGlib_1_0_gir_PACKAGES = gobject-2.0 gmodule-2.0 glib-2.0 gio-2.0 libsoup-gnome-2.4 json-glib-1.0
 DiscidentGlib_1_0_gir_CFLAGS = -I$(srcdir)
 DiscidentGlib_1_0_gir_LIBS = libdiscident-glib.la
 DiscidentGlib_1_0_gir_SCANNERFLAGS = --symbol-prefix=discident_ --identifier-prefix=Discident --pkg-export=discident-glib-1.0
diff --git a/discident-glib/discident-ean-glib.c b/discident-glib/discident-ean-glib.c
new file mode 100644
index 0000000..6d87dc2
--- /dev/null
+++ b/discident-glib/discident-ean-glib.c
@@ -0,0 +1,361 @@
+/*
+   Copyright (C) 2010 Bastien Nocera
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301  USA.
+
+   Authors: Bastien Nocera <hadess hadess net>
+
+ */
+
+#include <string.h>
+
+#include <libsoup/soup-gnome.h>
+#include <glib/gprintf.h>
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+
+#include "discident-ean-glib.h"
+#include "discident-error.h"
+
+#define APPID "19d78263508549b98db3809824f79c4d0fcad11b"
+#define EMULATED_VERSION "2.7.0"
+#define ORIGINAL_QUERY "http://redlaser.com:8008/getinfo.ashx?v="; EMULATED_VERSION "&app=rl&responseId=20100708&udid=" APPID
+#define SEARCH_URL "http://%s/searchresults.ashx";
+#define SEARCH_QUERY "barcode=%s&btype=EAN13&cachedimages=ebay_small,googlelogo_24,thefindlogo&currency=GBP&locale=en_GB&udid=" APPID "&v=" EMULATED_VERSION
+
+struct DiscidentEanPrivate {
+	char *server;
+	gboolean enabled;
+	char *message;
+};
+
+G_DEFINE_TYPE (DiscidentEan, discident_ean, G_TYPE_OBJECT)
+
+static void
+discident_ean_finalize (GObject *object)
+{
+	DiscidentEan *ean;
+
+	ean = DISCIDENT_EAN (object);
+
+	g_free (ean->priv->server);
+	ean->priv->server = NULL;
+
+	g_free (ean->priv->message);
+	ean->priv->message = NULL;
+
+	G_OBJECT_CLASS (discident_ean_parent_class)->finalize (object);
+}
+
+static void
+discident_ean_class_init (DiscidentEanClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->finalize = discident_ean_finalize;
+
+	g_type_class_add_private (klass, sizeof (DiscidentEanPrivate));
+}
+
+static void
+discident_ean_init (DiscidentEan *ean)
+{
+	ean->priv = G_TYPE_INSTANCE_GET_PRIVATE ((ean), DISCIDENT_TYPE_EAN, DiscidentEanPrivate);
+	ean->priv->enabled = TRUE;
+}
+
+static gboolean
+parse_login_response (DiscidentEan *ean,
+		      char         *response)
+{
+	GHashTable *hash;
+	char **items;
+	guint i;
+	const char *disable_capture;
+
+	hash = g_hash_table_new_full (g_str_hash, g_str_equal,
+				      g_free, g_free);
+
+	/* Remove trailing '|' */
+	if (response[strlen (response) - 1] == '|')
+		response[strlen (response) - 1] = '\0';
+
+	items = g_strsplit (response, ",", -1);
+	for (i = 0; items[i] != NULL; i++) {
+		char **vars;
+
+		vars = g_strsplit (items[i], "=", -1);
+		if (items[0] == NULL || items[1] == NULL) {
+			g_strfreev (items);
+			continue;
+		}
+		g_hash_table_insert (hash, g_ascii_strdown (vars[0], -1), g_strdup (vars[1]));
+		g_strfreev (vars);
+	}
+	g_strfreev (items);
+
+	ean->priv->server = g_strdup (g_hash_table_lookup (hash, "ssvr"));
+	if (ean->priv->server == NULL) {
+		g_hash_table_destroy (hash);
+		return FALSE;
+	}
+	ean->priv->message = g_strdup (g_hash_table_lookup (hash, "disablecapturemessage"));
+	disable_capture = g_hash_table_lookup (hash, "disablecapture");
+	if (disable_capture != NULL &&
+	    g_ascii_strncasecmp (disable_capture, "no", 2) != 0) {
+		ean->priv->enabled = FALSE;
+	}
+
+	g_hash_table_destroy (hash);
+
+	return TRUE;
+}
+
+gboolean
+discident_ean_login (DiscidentEan *ean,
+		     GError      **error)
+{
+	SoupSession *session;
+	SoupMessage *msg;
+	char *response;
+	int ret;
+
+	g_return_val_if_fail (DISCIDENT_IS_EAN (ean), FALSE);
+
+	session = soup_session_sync_new_with_options (
+	    SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_PROXY_RESOLVER_GNOME,
+	    NULL);
+
+	msg = soup_message_new ("GET", ORIGINAL_QUERY);
+	g_assert (msg != NULL);
+
+	response = NULL;
+
+	ret = soup_session_send_message (session, msg);
+	if (SOUP_STATUS_IS_SUCCESSFUL (ret) &&
+	    msg->response_body != NULL) {
+		response = g_strdup (msg->response_body->data);
+	}
+	g_object_unref (msg);
+	g_object_unref (session);
+
+	if (response == NULL) {
+		g_set_error (error, SOUP_HTTP_ERROR, ret, "Could not login to EAN service");
+		return FALSE;
+	}
+
+	if (parse_login_response (ean, response) == FALSE) {
+		g_set_error (error, DISCIDENT_ERROR, DISCIDENT_ERROR_PARSE, "Failed to parse login response from EAN service");
+		g_free (response);
+		return FALSE;
+	}
+	g_free (response);
+
+	return TRUE;
+}
+
+static char *
+get_search_uri (DiscidentEan *ean)
+{
+	return g_strdup_printf (SEARCH_URL, ean->priv->server);
+}
+
+static char *
+get_post_data (const char *barcode)
+{
+	return g_strdup_printf (SEARCH_QUERY, barcode);
+}
+
+static char *
+uncompress (const char *data,
+	    gssize      len)
+{
+	GConverter *converter;
+	GInputStream *input_stream, *stream;
+	GOutputStream *output;
+	char *ret;
+
+	input_stream = g_memory_input_stream_new ();
+	converter = G_CONVERTER (g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP));
+	stream = (GInputStream *) g_converter_input_stream_new (input_stream, converter);
+	output = (GOutputStream *) g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
+
+	g_memory_input_stream_add_data ((GMemoryInputStream *) input_stream,
+					g_memdup (data, len), len,
+					(GDestroyNotify) g_free);
+
+	if (!g_output_stream_splice (G_OUTPUT_STREAM (output), stream,
+				     G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE,
+				     NULL, NULL)) {
+		g_object_unref (output);
+		return NULL;
+	}
+
+	ret = g_strdup (g_memory_output_stream_get_data ((GMemoryOutputStream *) output));
+	g_object_unref (output);
+
+	return ret;
+}
+
+static gboolean
+parse_lookup_response (const char *response,
+		       char        **ret_title,
+		       char        **ret_img_url)
+{
+	xmlDocPtr doc;
+	xmlChar *title, *img_url;
+
+	doc = xmlParseMemory (response, strlen (response));
+	if (doc == NULL)
+		doc = xmlRecoverMemory (response, strlen (response));
+
+	if(!doc ||
+	   !doc->children ||
+	   !doc->children->name ||
+	   g_ascii_strcasecmp ((char *)doc->children->name, "response") != 0) {
+		if (doc != NULL)
+			xmlFreeDoc (doc);
+		return FALSE;
+	}
+
+	title = xmlGetProp (doc->children, (const xmlChar *) "title");
+	if (title == NULL) {
+		xmlFreeDoc (doc);
+		return FALSE;
+	}
+	*ret_title = (char *) title;
+
+	img_url = xmlGetProp (doc->children, (const xmlChar *) "imageUrl");
+	if (img_url != NULL)
+		*ret_img_url = (char *) img_url;
+
+	xmlFreeDoc (doc);
+
+	return TRUE;
+}
+
+gboolean
+discident_ean_lookup (DiscidentEan *ean,
+		      const char   *barcode,
+		      char        **title,
+		      char        **img_url,
+		      GError      **error)
+{
+	SoupSession *session;
+	SoupMessage *msg;
+	char *uri, *data, *response;
+	int ret;
+
+	g_return_val_if_fail (DISCIDENT_IS_EAN (ean), FALSE);
+	g_return_val_if_fail (title != NULL, FALSE);
+
+	session = soup_session_sync_new_with_options (
+	    SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_PROXY_RESOLVER_GNOME,
+	    NULL);
+
+	uri = get_search_uri (ean);
+	msg = soup_message_new ("POST", uri);
+	g_message ("uri %s", uri);
+	g_free (uri);
+
+	data = get_post_data (barcode);
+	g_message ("post %s", data);
+	soup_message_set_request (msg, "application/x-www-form-urlencoded",
+				  SOUP_MEMORY_TAKE, data, strlen (data));
+
+	response = NULL;
+
+	ret = soup_session_send_message (session, msg);
+	if (SOUP_STATUS_IS_SUCCESSFUL (ret) &&
+	    msg->response_body != NULL) {
+		response = uncompress (msg->response_body->data,
+				       msg->response_body->length);
+		/* Probably not compressed */
+		if (response == NULL) {
+			response = g_strndup (msg->response_body->data,
+					      msg->response_body->length);
+		}
+	}
+	g_object_unref (msg);
+	g_object_unref (session);
+
+	if (response == NULL) {
+		g_set_error (error, SOUP_HTTP_ERROR, ret, "Could not login to EAN service");
+		return FALSE;
+	}
+
+	if (parse_lookup_response (response, title, img_url) == FALSE) {
+		g_set_error (error, DISCIDENT_ERROR, DISCIDENT_ERROR_PARSE, "Failed to parse response from EAN service");
+		return FALSE;
+	}
+
+	g_free (response);
+
+	return TRUE;
+}
+
+DiscidentEan *
+discident_ean_new (void)
+{
+	return g_object_new (DISCIDENT_TYPE_EAN, NULL);
+}
+
+#if 0
+void
+discident_ean_login_async (DiscidentEan        *ean,
+			   GCancellable        *cancellable,
+			   GAsyncReadyCallback  callback,
+			   gpointer             user_data)
+{
+	GSimpleAsyncResult *simple;
+
+	simple = g_simple_async_result_new (G_OBJECT (directory),
+					    callback,
+					    user_data,
+					    discident_ean_login_async);
+
+	if (cancellable != NULL) {
+		g_object_set_data_full (G_OBJECT (simple), "cancellable",
+					g_object_ref (cancellable), g_object_unref);
+	}
+
+	discident_get_gtin_file_async (directory,
+				       cancellable,
+				       on_discident_got_gtin,
+				       simple);
+}
+
+gboolean
+discident_ean_login_finish (DiscidentEan        *ean,
+			    GAsyncResult        *res,
+			    GError             **error)
+{
+	GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+	char *ret;
+
+	g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == discident_ean_login_async);
+
+	ret = NULL;
+
+	if (g_simple_async_result_propagate_error (simple, error))
+		goto out;
+
+	ret = g_simple_async_result_get_op_res_gpointer (simple);
+
+out:
+	return ret;
+}
+#endif
diff --git a/discident-glib/discident-ean-glib.h b/discident-glib/discident-ean-glib.h
new file mode 100644
index 0000000..5f3898c
--- /dev/null
+++ b/discident-glib/discident-ean-glib.h
@@ -0,0 +1,61 @@
+/*
+   Copyright (C) 2010 Bastien Nocera
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301  USA.
+
+   Authors: Bastien Nocera <hadess hadess net>
+
+ */
+
+#ifndef DISCIDENT_EAN_GLIB_H
+#define DISCIDENT_EAN_GLIB_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#define DISCIDENT_TYPE_EAN         (discident_ean_get_type ())
+#define DISCIDENT_EAN(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), DISCIDENT_TYPE_EAN, DiscidentEan))
+#define DISCIDENT_EAN_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), DISCIDENT_TYPE_EAN, DiscidentEanClass))
+#define DISCIDENT_IS_EAN(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), DISCIDENT_TYPE_EAN))
+#define DISCIDENT_IS_EAN_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), DISCIDENT_TYPE_EAN))
+#define DISCIDENT_EAN_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), DISCIDENT_TYPE_EAN, DiscidentEanClass))
+
+typedef struct DiscidentEanPrivate DiscidentEanPrivate;
+
+typedef struct {
+	GObject              parent;
+	DiscidentEanPrivate *priv;
+} DiscidentEan;
+
+typedef struct {
+	GObjectClass parent_class;
+} DiscidentEanClass;
+
+GType         discident_ean_get_type (void);
+DiscidentEan *discident_ean_new      (void);
+gboolean      discident_ean_login    (DiscidentEan *ean,
+				      GError      **error);
+
+gboolean       discident_ean_lookup (DiscidentEan *ean,
+				     const char   *barcode,
+				     char        **title,
+				     char        **img_url,
+				     GError      **error);
+
+G_END_DECLS
+
+#endif /* DISCIDENT_EAN_GLIB_H */
diff --git a/discident-glib/discident-glib.symbols b/discident-glib/discident-glib.symbols
index 7a1ebe2..7e0c03c 100644
--- a/discident-glib/discident-glib.symbols
+++ b/discident-glib/discident-glib.symbols
@@ -6,3 +6,8 @@ discident_get_title_file_async
 discident_get_title_file_finish
 generate_hash
 discident_get_title_for_gtin
+discident_ean_get_type
+discident_ean_new
+discident_ean_login
+discident_ean_lookup
+discident_error_quark
diff --git a/discident-glib/test-diglib.c b/discident-glib/test-diglib.c
index 57c3eee..3494e58 100644
--- a/discident-glib/test-diglib.c
+++ b/discident-glib/test-diglib.c
@@ -4,6 +4,7 @@
 #include <glib.h>
 #include <gio/gio.h>
 #include <discident-glib.h>
+#include <discident-ean-glib.h>
 #include <discident-glib-private.h>
 
 static gboolean option_async = FALSE;
@@ -63,11 +64,12 @@ test_json (void)
 		const char *gtin;
 		const char *title;
 	} dvds[] = {
-		{ "3809DA3F-8323-2E48-70C6-861B34968674", NULL },
-		{ "5F03F0D5-6AB6-FDB4-43AE-825693898CFF", "The Kite Runner" },
-		{ "4C059E8A-37E4-493E-6272-F9A713DB5AA9", "55853 STRAIGHT_STORY" },
-		{ "724DAC2A-6BB7-21E8-2F85-BCD0A56ED995", "WOODSMAN" },
-		{ "D2740096-2507-09FC-EE0F-D577CBF98536", "ETRE_AVOIR" },
+		{ "3809DA3F-8323-2E48-70C6-861B34968674", "ALEXANDER_NEVSKY" },
+		/* Disabled as somebody busted the actual title
+		{ "5F03F0D5-6AB6-FDB4-43AE-825693898CFF", "The Kite Runner" }, */
+		{ "4C059E8A-37E4-493E-6272-F9A713DB5AA9", "Straight Story" },
+		{ "724DAC2A-6BB7-21E8-2F85-BCD0A56ED995", "The Woodsman" },
+		{ "D2740096-2507-09FC-EE0F-D577CBF98536", "Ã?tre et Avoir" },
 	};
 
 	for (i = 0; i < G_N_ELEMENTS (dvds); i++) {
@@ -76,6 +78,21 @@ test_json (void)
 }
 
 static void
+test_ean (void)
+{
+	DiscidentEan *ean;
+	char *title, *img_url;
+
+	ean = discident_ean_new ();
+	g_assert (discident_ean_login (ean, NULL) != FALSE);
+
+	/* The Little Book of Stress: Calm is for Wimps, Get Real, Get Stressed */
+	g_assert (discident_ean_lookup (ean, "9780091865856", &title, &img_url, NULL) != FALSE);
+	g_assert_cmpstr ("The Little Book of Stress: Calm is for Wimps, Get Real, Get Stressed by Rohan Candappa (Paperback, 1998)", ==, title);
+	g_assert (img_url != NULL);
+}
+
+static void
 discident_title_print (GObject *source_object,
 		      GAsyncResult *res,
 		      gpointer user_data)
@@ -152,6 +169,7 @@ int main (int argc, char **argv)
 	}
 
 	if (uris == NULL) {
+		g_test_add_func ("/discident/ean", test_ean);
 		g_test_add_func ("/discident/hash", test_hash);
 		g_test_add_func ("/discident/file_list", test_file_list);
 		g_test_add_func ("/discident/json", test_json);



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