[grilo-plugins] lua-factory: Load and initialise Lua sources



commit b06acd2b32f48103d53a88e0b82e75844879576c
Author: Victor Toso <me victortoso com>
Date:   Thu Oct 31 22:03:34 2013 -0200

    lua-factory: Load and initialise Lua sources
    
    - Find lua sources in the install path or in the directories specified
      by envvars
    - Run source a first time to get the source metadata provided by
      the script's 'source' table
    - Check if necessary API keys were provided by application, if
      applicable
    
    https://bugzilla.gnome.org/show_bug.cgi?id=711243

 Makefile.am                         |    1 +
 configure.ac                        |   47 ++
 src/Makefile.am                     |    6 +-
 src/lua-factory/Makefile.am         |   40 ++
 src/lua-factory/grl-lua-common.h    |   69 +++
 src/lua-factory/grl-lua-factory.c   |  818 +++++++++++++++++++++++++++++++++++
 src/lua-factory/grl-lua-factory.h   |   51 +++
 src/lua-factory/grl-lua-factory.xml |   10 +
 src/lua-factory/grl-lua-library.c   |   46 ++
 src/lua-factory/grl-lua-library.h   |   36 ++
 src/lua-factory/sources/Makefile.am |   16 +
 11 files changed, 1139 insertions(+), 1 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index ac5547f..d118c95 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -47,6 +47,7 @@ DISTCHECK_CONFIGURE_FLAGS = --enable-apple-trailers \
                             --enable-jamendo \
                             --enable-lastfm-albumart \
                             --enable-localmetadata \
+                            --enable-lua-factory \
                             --enable-magnatune \
                             --enable-metadata-store \
                             --enable-optical-media \
diff --git a/configure.ac b/configure.ac
index 23678e0..abced34 100644
--- a/configure.ac
+++ b/configure.ac
@@ -138,6 +138,8 @@ PKG_CHECK_MODULES(GDATA, libgdata >= 0.7.0, HAVE_GDATA=yes, HAVE_GDATA=no)
 
 PKG_CHECK_MODULES(LIBSOUP, libsoup-2.4, HAVE_LIBSOUP=yes, HAVE_LIBSOUP=no)
 
+PKG_CHECK_MODULES(LUA, lua >= 5.2.0, HAVE_LUA=yes, HAVE_LUA=no)
+
 PKG_CHECK_MODULES(GTHREAD, gthread-2.0, HAVE_GTHREAD=yes, HAVE_GTHREAD=no)
 
 PKG_CHECK_MODULES(OAUTH, oauth, HAVE_OAUTH=yes, HAVE_OAUTH=no)
@@ -828,6 +830,49 @@ DEPS_MAGNATUNE_LIBS="$DEPS_LIBS $GRLNET_LIBS $SQLITE_LIBS"
 AC_SUBST(DEPS_MAGNATUNE_LIBS)
 
 # ----------------------------------------------------------
+# BUILD LUA FACTORY PLUGIN
+# ----------------------------------------------------------
+
+AC_ARG_ENABLE(lua_factory,
+        AC_HELP_STRING([--enable-lua-factory],
+                [enable Lua plugins (default: auto)]),
+        [
+                case "$enableval" in
+                     yes)
+                        if test "x$HAVE_LUA" = "xno"; then
+                           AC_MSG_ERROR([lua not found, install it or use --disable-lua-factory])
+                        fi
+                        ;;
+                esac
+        ],
+        [
+                if test "x$HAVE_LUA" = "xyes"; then
+                  enable_lua_factory=yes
+                else
+                  enable_lua_factory=no
+                fi
+        ])
+
+AM_CONDITIONAL([LUA_FACTORY_PLUGIN], [test "x$enable_lua_factory" = "xyes"])
+GRL_PLUGINS_ALL="$GRL_PLUGINS_ALL lua_factory"
+if test "x$enable_lua_factory" = "xyes"
+then
+       GRL_PLUGINS_ENABLED="$GRL_PLUGINS_ENABLED lua_factory"
+fi
+
+LUA_FACTORY_PLUGIN_ID="grl-lua-factory"
+AC_SUBST(LUA_FACTORY_PLUGIN_ID)
+AC_DEFINE_UNQUOTED([LUA_FACTORY_PLUGIN_ID], ["$LUA_FACTORY_PLUGIN_ID"], [Lua Factory plugin ID])
+
+DEPS_LUA_FACTORY_CFLAGS="$DEPS_CFLAGS $LUA_CFLAGS"
+AC_SUBST(DEPS_LUA_FACTORY_CFLAGS)
+DEPS_LUA_FACTORY_LIBS="$DEPS_LIBS $LUA_LIBS"
+AC_SUBST(DEPS_LUA_FACTORY_LIBS)
+
+LUA_FACTORY_SOURCE_LOCATION="grilo-plugins/${LUA_FACTORY_PLUGIN_ID}"
+AC_SUBST(LUA_FACTORY_SOURCE_LOCATION)
+
+# ----------------------------------------------------------
 # BUILD METADATA-STORE PLUGIN
 # ----------------------------------------------------------
 
@@ -1320,6 +1365,8 @@ AC_CONFIG_FILES([
   src/jamendo/Makefile
   src/lastfm-albumart/Makefile
   src/local-metadata/Makefile
+  src/lua-factory/Makefile
+  src/lua-factory/sources/Makefile
   src/magnatune/Makefile
   src/metadata-store/Makefile
   src/optical-media/Makefile
diff --git a/src/Makefile.am b/src/Makefile.am
index 467b6fd..93492b6 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -50,6 +50,10 @@ if LOCALMETADATA_PLUGIN
 SUBDIRS += local-metadata
 endif
 
+if LUA_FACTORY_PLUGIN
+SUBDIRS += lua-factory
+endif
+
 if MAGNATUNE_PLUGIN
 SUBDIRS += magnatune 
 endif
@@ -108,7 +112,7 @@ endif
 
 DIST_SUBDIRS = \
    apple-trailers bliptv bookmarks dmap filesystem flickr freebox gravatar guardian-videos jamendo \
-   lastfm-albumart local-metadata magnatune metadata-store optical-media   \
+   lastfm-albumart local-metadata lua-factory magnatune metadata-store optical-media   \
    pocket podcasts raitv shoutcast tmdb tracker upnp vimeo youtube
 
 MAINTAINERCLEANFILES = \
diff --git a/src/lua-factory/Makefile.am b/src/lua-factory/Makefile.am
new file mode 100644
index 0000000..0af5360
--- /dev/null
+++ b/src/lua-factory/Makefile.am
@@ -0,0 +1,40 @@
+#
+# Makefile.am
+#
+# Author: Victor Toso <me victortoso com>
+#
+# Copyright (C) 2013 Victor Toso. All rights reserved.
+
+SUBDIRS = sources
+
+ext_LTLIBRARIES = libgrlluafactory.la
+
+libgrlluafactory_la_CFLAGS =                                           \
+       $(DEPS_LUA_FACTORY_CFLAGS)                                      \
+       -DLUA_FACTORY_SOURCE_LOCATION=\"@LUA_FACTORY_SOURCE_LOCATION \"
+
+libgrlluafactory_la_LIBADD = $(DEPS_LUA_FACTORY_LIBS)
+
+libgrlluafactory_la_LDFLAGS =  \
+       -no-undefined           \
+       -module                 \
+       -avoid-version
+
+libgrlluafactory_la_SOURCES =                                  \
+       grl-lua-factory.c                                       \
+       grl-lua-factory.h                                       \
+       grl-lua-library.c                                       \
+       grl-lua-library.h                                       \
+       grl-lua-common.h                                        \
+       lua-library/lua-libraries.h
+
+extdir                 = $(GRL_PLUGINS_DIR)
+luafactoryxmldir       = $(GRL_PLUGINS_DIR)
+luafactoryxml_DATA     = $(LUA_FACTORY_PLUGIN_ID).xml
+
+EXTRA_DIST = $(luafactoryxml_DATA)
+
+DIST_SUBDIRS = sources
+
+MAINTAINERCLEANFILES = *.in *~
+DISTCLEANFILES = $(MAINTAINERCLEANFILES)
diff --git a/src/lua-factory/grl-lua-common.h b/src/lua-factory/grl-lua-common.h
new file mode 100644
index 0000000..f5f472c
--- /dev/null
+++ b/src/lua-factory/grl-lua-common.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2013 Victor Toso.
+ *
+ * Contact: Victor Toso <me victortoso 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
+
+#ifndef _GRL_LUA_LIBRARY_COMMON_H_
+#define _GRL_LUA_LIBRARY_COMMON_H_
+
+#include "grl-lua-library.h"
+#include <glib/gi18n-lib.h>
+
+typedef enum {
+  LUA_SEARCH,
+  LUA_BROWSE,
+  LUA_QUERY,
+  LUA_RESOLVE,
+  LUA_SOURCE_INIT,
+  LUA_NUM_OPERATIONS
+} LuaOperationType;
+
+/**
+* OperationSpec:
+* @source: The GrlLuaFactorySource of operation.
+* @operation_id: The operation_id of operation that generate this structure.
+* @op_type: Witch operation its being executed.
+* @cb: union to user callback. The function parameters depends on operation.
+* @content: Save the current user media if already have one.
+* @user_data: User data passed in user defined callback.
+* @error_code: To set GRL_CORE_ERROR of the operation.
+*
+* This structure is used to save important data in the communication between
+* lua-factory and lua-libraries.
+*/
+typedef struct _OperationSpec {
+  GrlSource *source;
+  guint operation_id;
+  GrlOperationOptions *options;
+  GList *keys;
+  LuaOperationType op_type;
+  union {
+    GrlSourceResultCb result;
+    GrlSourceResolveCb resolve;
+  } cb;
+  GrlMedia *media;
+  gpointer user_data;
+  guint error_code;
+} OperationSpec;
+
+#endif /* _GRL_LUA_LIBRARY_COMMON_H_ */
diff --git a/src/lua-factory/grl-lua-factory.c b/src/lua-factory/grl-lua-factory.c
new file mode 100644
index 0000000..4baad1b
--- /dev/null
+++ b/src/lua-factory/grl-lua-factory.c
@@ -0,0 +1,818 @@
+/*
+ * Copyright (C) 2013 Victor Toso.
+ *
+ * Contact: Victor Toso <me victortoso 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 "grl-lua-common.h"
+#include "grl-lua-factory.h"
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+#define GRL_LUA_FACTORY_SOURCE_GET_PRIVATE(object)           \
+  (G_TYPE_INSTANCE_GET_PRIVATE((object),                     \
+                               GRL_LUA_FACTORY_SOURCE_TYPE,  \
+                               GrlLuaFactorySourcePrivate))
+
+/* --------- Logging  -------- */
+
+#define GRL_LOG_DOMAIN_DEFAULT lua_factory_log_domain
+GRL_LOG_DOMAIN_STATIC (lua_factory_log_domain);
+
+#define ENV_LUA_SOURCES_PATH  "GRL_LUA_SOURCES_PATH"
+#define LUA_FACTORY_PLUGIN_ID "grl-lua-factory"
+
+/* --- Main table --- */
+#define LUA_SOURCE_TABLE            "source"
+#define LUA_SOURCE_ID               "id"
+#define LUA_SOURCE_NAME             "name"
+#define LUA_SOURCE_DESCRIPTION      "description"
+#define LUA_SOURCE_SUPPORTED_MEDIA  "supported_media"
+#define LUA_SOURCE_ICON             "icon"
+#define LUA_SOURCE_CONFIG_KEYS      "config_keys"
+#define LUA_SOURCE_SUPPORTED_KEYS   "supported_keys"
+#define LUA_SOURCE_SLOW_KEYS        "slow_keys"
+#define LUA_SOURCE_RESOLVE_KEYS     "resolve_keys"
+#define LUA_SOURCE_MODULES_DEPS     "dependencies"
+#define LUA_REQUIRED_TABLE          "required"
+#define LUA_OPTIONAL_TABLE          "optional"
+
+static const char *LUA_SOURCE_OPERATION[LUA_NUM_OPERATIONS] = {
+  [LUA_SEARCH] = "grl_source_search",
+  [LUA_BROWSE] = "grl_source_browse",
+  [LUA_QUERY] = "grl_source_query",
+  [LUA_RESOLVE] = "grl_source_resolve",
+  [LUA_SOURCE_INIT] = "grl_source_init"
+};
+
+struct _GrlLuaFactorySourcePrivate {
+  lua_State *l_st;
+  gboolean fn[LUA_NUM_OPERATIONS];
+  GList *supported_keys;
+  GList *slow_keys;
+  GList *resolve_keys;
+  GrlMediaType resolve_type;
+  GHashTable *config_keys;
+  GrlConfig *configs;
+};
+
+static GList *get_lua_sources (void);
+
+static GrlLuaFactorySource *grl_lua_factory_source_new (gchar *lua_plugin_path,
+                                                        GList *configs);
+
+static gint lua_plugin_source_info (lua_State *L,
+                                    gchar **source_id,
+                                    gchar **source_name,
+                                    gchar **source_desc,
+                                    GrlMediaType *source_supported_media,
+                                    GIcon **source_icon);
+
+static gint lua_plugin_source_operations (lua_State *L,
+                                          gboolean fn[LUA_NUM_OPERATIONS]);
+
+static gint lua_plugin_source_all_dependencies (lua_State *L);
+
+static gint lua_plugin_source_all_keys (lua_State *L,
+                                        GList **supported_keys,
+                                        GList **slow_keys,
+                                        GList **resolve_keys,
+                                        GrlMediaType *resolve_type,
+                                        GHashTable **config_keys);
+
+static void grl_lua_factory_source_finalize (GObject *object);
+
+static const GList *grl_lua_factory_source_supported_keys (GrlSource *source);
+
+static GrlSupportedOps
+grl_lua_factory_source_supported_operations (GrlSource *source);
+
+static GrlConfig *merge_all_configs (const gchar *source_id,
+                                     GHashTable *source_configs,
+                                     GList *available_configs);
+
+static gboolean all_mandatory_options_has_value (GHashTable *source_configs,
+                                                 GrlConfig *merged_configs);
+
+/* ================== Lua-Factory Plugin  ================================== */
+
+static gboolean
+grl_lua_factory_plugin_init (GrlRegistry *registry,
+                             GrlPlugin *plugin,
+                             GList *configs)
+{
+  GList *it = NULL;
+  GList *lua_sources = NULL;
+  GError *err = NULL;
+  gboolean source_loaded = FALSE;
+
+  GRL_LOG_DOMAIN_INIT (lua_factory_log_domain, "lua-factory");
+
+  GRL_DEBUG ("grl_lua_factory_plugin_init");
+
+  lua_sources = get_lua_sources ();
+  if (!lua_sources)
+    return TRUE;
+
+  for (it = lua_sources; it; it = g_list_next (it)) {
+    GrlLuaFactorySource *source;
+
+    source = grl_lua_factory_source_new (it->data, configs);
+    if (source == NULL) {
+      GRL_DEBUG ("Fail to initialize.");
+      continue;
+    }
+
+    if (!grl_registry_register_source (registry, plugin,
+                                       GRL_SOURCE (source), &err)) {
+      GRL_DEBUG ("Fail to register source: %s", err->message);
+      g_error_free (err);
+      continue;
+    }
+
+    source_loaded = TRUE;
+    GRL_DEBUG ("Successfully initialized: %s",
+               grl_source_get_id (GRL_SOURCE (source)));
+  }
+  g_list_free_full (lua_sources, g_free);
+  return source_loaded;
+}
+
+GRL_PLUGIN_REGISTER (grl_lua_factory_plugin_init, NULL, LUA_FACTORY_PLUGIN_ID);
+
+/* ================== Lua-Factory GObject ================================== */
+
+static GrlLuaFactorySource *
+grl_lua_factory_source_new (gchar *lua_plugin_path,
+                            GList *configs)
+{
+  GrlLuaFactorySource *source = NULL;
+  lua_State *L = NULL;
+  GHashTable *config_keys = NULL;
+  gchar *source_id = NULL;
+  gchar *source_name = NULL;
+  gchar *source_desc = NULL;
+  GIcon *source_icon = NULL;
+  GrlMediaType source_supported_media = GRL_MEDIA_TYPE_ALL;
+  gint ret = 0;
+
+  GRL_DEBUG ("grl_lua_factory_source_new");
+
+  L = luaL_newstate ();
+  if (L == NULL) {
+    GRL_WARNING ("Unable to create new lua state.");
+    return NULL;
+  }
+
+  GRL_DEBUG ("Loading '%s'", lua_plugin_path);
+
+  /* Standard Lua libraries */
+  luaL_openlibs (L);
+
+  /* Grilo library */
+  luaL_requiref (L, GRILO_LUA_LIBRARY_NAME, &luaopen_grilo, TRUE);
+  lua_pop (L, 1);
+
+  /* Load the plugin */
+  ret = luaL_loadfile (L, lua_plugin_path);
+  if (ret != LUA_OK) {
+    GRL_WARNING ("[%s] failed to load: %s", lua_plugin_path, lua_tostring (L, -1));
+    goto bail;
+  }
+
+  ret = lua_pcall (L, 0, 0, 0);
+  if (ret != LUA_OK) {
+    GRL_WARNING ("[%s] failed to run: %s", lua_plugin_path, lua_tostring (L, -1));
+    goto bail;
+  }
+
+  ret = lua_plugin_source_info (L, &source_id, &source_name, &source_desc,
+                                &source_supported_media, &source_icon);
+  if (ret != LUA_OK)
+    goto bail;
+
+  GRL_DEBUG ("source_info ok! source_id: '%s'", source_id);
+
+  source = g_object_new (GRL_LUA_FACTORY_SOURCE_TYPE,
+                         "source-id", source_id,
+                         "source-name", source_name,
+                         "source-desc", source_desc,
+                         "supported-media", source_supported_media,
+                         "source-icon", source_icon,
+                         NULL);
+  g_free (source_name);
+  g_free (source_desc);
+
+  ret = lua_plugin_source_operations (L, source->priv->fn);
+  if (ret != LUA_OK)
+    goto bail;
+
+  ret = lua_plugin_source_all_dependencies (L);
+  if (ret != LUA_OK)
+    goto bail;
+
+  ret = lua_plugin_source_all_keys (L,
+                                    &source->priv->supported_keys,
+                                    &source->priv->slow_keys,
+                                    &source->priv->resolve_keys,
+                                    &source->priv->resolve_type,
+                                    &config_keys);
+  if (ret != LUA_OK)
+    goto bail;
+
+  source->priv->configs = merge_all_configs (source_id, config_keys, configs);
+  if (!all_mandatory_options_has_value (config_keys, source->priv->configs))
+    goto bail;
+
+  g_free (source_id);
+  source->priv->config_keys = config_keys;
+  source->priv->l_st = L;
+  return source;
+
+bail:
+  if (source != NULL) {
+    if (config_keys != NULL)
+      g_hash_table_unref (config_keys);
+
+    if (source->priv->configs != NULL)
+      g_object_unref (source->priv->configs);
+
+    g_list_free (source->priv->resolve_keys);
+    g_list_free (source->priv->supported_keys);
+    g_list_free (source->priv->slow_keys);
+  }
+
+  g_free (source_id);
+  lua_close (L);
+  return NULL;
+}
+
+static void
+grl_lua_factory_source_class_init (GrlLuaFactorySourceClass *klass)
+{
+  GObjectClass *g_class = G_OBJECT_CLASS (klass);
+  GrlSourceClass *source_class = GRL_SOURCE_CLASS (klass);
+
+  g_class->finalize = grl_lua_factory_source_finalize;
+
+  source_class->supported_keys = grl_lua_factory_source_supported_keys;
+  source_class->supported_operations = grl_lua_factory_source_supported_operations;
+
+  g_type_class_add_private (klass, sizeof (GrlLuaFactorySourcePrivate));
+}
+
+G_DEFINE_TYPE (GrlLuaFactorySource, grl_lua_factory_source, GRL_TYPE_SOURCE);
+
+static void
+grl_lua_factory_source_init (GrlLuaFactorySource *source)
+{
+  source->priv = GRL_LUA_FACTORY_SOURCE_GET_PRIVATE (source);
+}
+
+static void
+grl_lua_factory_source_finalize (GObject *object)
+{
+  GrlLuaFactorySource *source = GRL_LUA_FACTORY_SOURCE (object);
+
+  if (source->priv->configs)
+    g_object_unref (source->priv->configs);
+
+  if (source->priv->config_keys)
+    g_hash_table_unref (source->priv->config_keys);
+
+  g_list_free (source->priv->resolve_keys);
+  g_list_free (source->priv->supported_keys);
+  g_list_free (source->priv->slow_keys);
+  lua_close (source->priv->l_st);
+
+  G_OBJECT_CLASS (grl_lua_factory_source_parent_class)->finalize (object);
+}
+
+/* ======================= Utilities ======================================= */
+
+static GList *
+table_array_to_list (lua_State *L,
+                     const gchar *array_name)
+{
+  GList *list = NULL;
+  gint i = 0;
+  gint array_len = 0;
+
+  lua_pushstring (L, array_name);
+  lua_gettable (L, -2);
+
+  if (lua_istable (L, -1)) {
+    array_len = luaL_len (L, -1);
+
+    for (i = 0; i < array_len; i++) {
+      lua_pushinteger (L, i + 1);
+      lua_gettable (L, -2);
+      if (lua_isstring (L, -1)) {
+        list = g_list_prepend (list, g_strdup (lua_tostring (L, -1)));
+      }
+      lua_pop (L, 1);
+    }
+  }
+  lua_pop (L, 1);
+
+  return g_list_reverse (list);
+}
+
+static gboolean
+lua_module_exists (const gchar *lua_module)
+{
+  gboolean exists = TRUE;
+  lua_State *L;
+
+  L = luaL_newstate ();
+  if (L == NULL) {
+    GRL_WARNING ("Unable to create new lua state.");
+    return FALSE;
+  }
+  luaL_openlibs (L);
+
+  lua_getglobal (L, "require");
+  lua_pushstring (L, lua_module);
+  if (lua_pcall (L, 1, 0, 0) != LUA_OK) {
+    GRL_DEBUG ("%s", lua_tolstring (L, -1, NULL));
+    exists = FALSE;
+    lua_pop (L, 1);
+  }
+
+  lua_close (L);
+  return exists;
+}
+
+static GList *
+get_lua_sources (void)
+{
+  GList *it_path = NULL;
+  GList *lua_sources = NULL;
+  GList *l_locations = NULL;
+  GDir *dir = NULL;
+  gint i = 0;
+  const gchar *envvar = NULL;
+  const gchar *it_file = NULL;
+
+  GRL_DEBUG ("get_lua_sources");
+
+  envvar = g_getenv (ENV_LUA_SOURCES_PATH);
+
+  if (envvar != NULL) {
+    gchar **local_dirs;
+
+    /* Environment-only plugins */
+    GRL_DEBUG ("'%s' %s", ENV_LUA_SOURCES_PATH,
+               "is setted - Getting lua-sources only from there.");
+    local_dirs = g_strsplit (envvar, G_SEARCHPATH_SEPARATOR_S, -1);
+    if (local_dirs) {
+      while (local_dirs[i] != NULL) {
+        l_locations = g_list_prepend (l_locations, g_strdup (local_dirs[i]));
+        i++;
+      }
+      g_strfreev (local_dirs);
+    }
+  } else {
+    const gchar *const *system_dirs = NULL;
+
+    /* System locations */
+    for (system_dirs = g_get_system_data_dirs ();
+         *system_dirs != NULL;
+         system_dirs++) {
+
+      l_locations = g_list_prepend (l_locations,
+                                    g_build_filename (*system_dirs,
+                                                      LUA_FACTORY_SOURCE_LOCATION,
+                                                      NULL));
+    }
+    /* User locations */
+    l_locations = g_list_prepend (l_locations,
+                                  g_build_filename (g_get_user_data_dir (),
+                                                    LUA_FACTORY_SOURCE_LOCATION,
+                                                    NULL));
+  }
+
+  for (it_path = l_locations; it_path; it_path = it_path->next) {
+    dir = g_dir_open (it_path->data, 0, NULL);
+    if (dir == NULL)
+      continue;
+
+    for (it_file = g_dir_read_name (dir);
+         it_file;
+         it_file = g_dir_read_name (dir)) {
+      if (g_str_has_suffix (it_file, ".lua")) {
+        lua_sources = g_list_prepend (lua_sources,
+                                      g_build_filename (it_path->data,
+                                                        it_file, NULL));
+      }
+    }
+    g_dir_close (dir);
+  }
+
+  g_list_free_full (l_locations, g_free);
+  return g_list_reverse (lua_sources);
+}
+
+/* all_mandatory_options_has_value
+ *
+ * If source_configs has mandatory options, check in our merged_configs
+ * if those mandatory options are settled.
+ * If any mandatory option is not settled, return FALSE.
+ */
+static gboolean
+all_mandatory_options_has_value (GHashTable *source_configs,
+                                 GrlConfig *merged_configs)
+{
+  const gchar *key = NULL;
+  const gchar *is_mandatory = NULL;
+  GList *list_keys = NULL;
+  GList *it_keys = NULL;
+
+  list_keys = (source_configs != NULL) ?
+              g_hash_table_get_keys (source_configs) : NULL;
+  for (it_keys = list_keys; it_keys; it_keys = g_list_next (it_keys)) {
+    key = it_keys->data;
+    is_mandatory = g_hash_table_lookup (source_configs, key);
+
+    if (g_strcmp0 (is_mandatory, "true") == 0
+        && grl_config_get_string (merged_configs, key) == NULL) {
+
+      g_list_free (list_keys);
+      return FALSE;
+    }
+  }
+  g_list_free (list_keys);
+  return TRUE;
+}
+
+/* Insert in @into all the @options in @from not already present */
+static void
+merge_config (GList *options,
+              GrlConfig *into,
+              GrlConfig *from)
+{
+  gchar *value = NULL;
+
+  if (from == NULL)
+    return;
+
+  while (options) {
+    if (!grl_config_has_param (into, options->data) &&
+        grl_config_has_param (from, options->data)) {
+      value = grl_config_get_string (from, options->data);
+      grl_config_set_string (into, options->data, value);
+      g_free (value);
+    }
+    options = g_list_next (options);
+  }
+}
+
+/* Create a config with default values from source and values from application.
+ * The @source_configs hash table has as keys, the config keys of @source_id
+ * source. This hash table may have keys with default values or only boolean
+ * values to point mandatory and optional config keys. */
+static GrlConfig *
+merge_all_configs (const gchar *source_id,
+                   GHashTable *source_configs,
+                   GList *available_configs)
+{
+  GList *list_all = NULL;
+  GList *list_generic = NULL;
+  GList *list_specific = NULL;
+  GList *it_config = NULL;
+  GList *options = NULL;
+  GrlConfig *merged_config = NULL;
+
+  /* From available configs get specific and generic options */
+  while (available_configs) {
+    gchar *config_source_id;
+
+    config_source_id = grl_config_get_source (available_configs->data);
+
+    if (config_source_id == NULL) {
+      list_generic = g_list_prepend (list_generic, available_configs->data);
+    } else if (g_strcmp0 (config_source_id, source_id) == 0) {
+      list_specific = g_list_prepend (list_specific, available_configs->data);
+    }
+
+    g_free (config_source_id);
+    available_configs = g_list_next (available_configs);
+  }
+
+  list_all = g_list_concat (g_list_reverse (list_specific),
+                            g_list_reverse (list_generic));
+
+  /* From source configs get default configs */
+  if (source_configs != NULL) {
+    GList *list_keys = NULL;
+    GList *it_keys = NULL;
+
+    list_keys = g_hash_table_get_keys (source_configs);
+    for (it_keys = list_keys; it_keys; it_keys = g_list_next (it_keys)) {
+      options = g_list_append (options, g_strdup (it_keys->data));
+    }
+  }
+
+  /* Now merge them */
+  merged_config = grl_config_new (LUA_FACTORY_PLUGIN_ID, source_id);
+  for (it_config = list_all; it_config; it_config = g_list_next (it_config)) {
+    merge_config (options, merged_config, it_config->data);
+  }
+  g_list_free (list_all);
+
+  if (options)
+    g_list_free_full (options, g_free);
+
+  return merged_config;
+}
+
+/* Get from the main table of the plugin
+ * the mandatory information to create a source. */
+static gint
+lua_plugin_source_info (lua_State *L,
+                        gchar **source_id,
+                        gchar **source_name,
+                        gchar **source_desc,
+                        GrlMediaType *source_supported_media,
+                        GIcon **source_icon)
+{
+  const char *lua_source_id = NULL;
+  const char *lua_source_name = NULL;
+  const char *lua_source_desc = NULL;
+  const char *lua_source_icon = NULL;
+  const char *lua_source_media = NULL;
+
+  GRL_DEBUG ("lua_plugin_source_info");
+
+  lua_getglobal (L, LUA_SOURCE_TABLE);
+  if (!lua_istable (L, -1)) {
+    GRL_DEBUG ("'%s' %s", LUA_SOURCE_TABLE, "table is not defined");
+    return !LUA_OK;
+  }
+
+  /* Source ID */
+  lua_getfield (L, -1, LUA_SOURCE_ID);
+  lua_source_id = lua_tolstring (L, -1, NULL);
+
+  /* Source Name */
+  lua_getfield (L, -2, LUA_SOURCE_NAME);
+  lua_source_name = lua_tolstring (L, -1, NULL);
+
+  /* Source Description */
+  lua_getfield (L, -3, LUA_SOURCE_DESCRIPTION);
+  lua_source_desc = lua_tolstring (L, -1, NULL);
+
+  /* Source Supported Media */
+  lua_getfield (L, -4, LUA_SOURCE_SUPPORTED_MEDIA);
+  lua_source_media = lua_tolstring (L, -1, NULL);
+
+  /* Source Icon */
+  lua_getfield (L, -5, LUA_SOURCE_ICON);
+  lua_source_icon = lua_tolstring (L, -1, NULL);
+
+  /* Remove source info and main table from stack */
+  lua_pop (L, 6);
+
+  if (lua_source_id == NULL
+      || lua_source_name == NULL
+      || lua_source_desc == NULL) {
+    GRL_DEBUG ("Lua source info is not well defined.");
+    return !LUA_OK;
+  }
+
+  *source_id = g_strdup (lua_source_id);
+  *source_name = g_strdup (lua_source_name);
+  *source_desc = g_strdup (lua_source_desc);
+
+  if (lua_source_media != NULL) {
+    if (g_strcmp0 (lua_source_media, "audio") == 0)
+      *source_supported_media = GRL_MEDIA_TYPE_AUDIO;
+    else if (g_strcmp0 (lua_source_media, "video") == 0)
+      *source_supported_media = GRL_MEDIA_TYPE_VIDEO;
+    else if (g_strcmp0 (lua_source_media, "image") == 0)
+      *source_supported_media = GRL_MEDIA_TYPE_IMAGE;
+    else if (g_strcmp0 (lua_source_media, "all") == 0)
+      *source_supported_media = GRL_MEDIA_TYPE_ALL;
+  }
+
+  if (lua_source_icon != NULL) {
+    GFile *file = g_file_new_for_uri (lua_source_icon);
+    *source_icon = g_file_icon_new (file);
+    g_object_unref (file);
+  }
+  return LUA_OK;
+}
+
+static gint
+lua_plugin_source_operations (lua_State *L,
+                              gboolean fn[LUA_NUM_OPERATIONS])
+{
+  gint i = 0;
+
+  GRL_DEBUG ("lua_plugin_source_operations");
+
+  /* Initialize fn array */
+  for (i = 0; i < LUA_NUM_OPERATIONS; i++) {
+    lua_getglobal (L, LUA_SOURCE_OPERATION[i]);
+    fn[i] = (lua_isfunction (L, -1)) ? TRUE : FALSE;
+    lua_pop (L, 1);
+  }
+
+  return LUA_OK;
+}
+
+static gint
+lua_plugin_source_all_dependencies (lua_State *L)
+{
+  GList *it = NULL;
+  GList *table_list = NULL;
+  gboolean module_fail = FALSE;
+
+  GRL_DEBUG ("lua_plugin_source_all_dependencies");
+
+  /* Dependencies are in the main table */
+  lua_getglobal (L, LUA_SOURCE_TABLE);
+
+  /* Check if lua modules dependencies are installed */
+  table_list = table_array_to_list (L, LUA_SOURCE_MODULES_DEPS);
+  if (table_list != NULL) {
+    gchar *lua_module = NULL;
+
+    for (it = table_list; it; it = g_list_next (it)) {
+      lua_module = it->data;
+
+      if (lua_module_exists (lua_module) == FALSE) {
+        module_fail = TRUE;
+        GRL_INFO ("%s %s", lua_module, "lua module is not installed");
+      }
+    }
+  }
+
+  g_list_free_full (table_list, g_free);
+  return (module_fail) ? !LUA_OK : LUA_OK;
+}
+
+static gint
+lua_plugin_source_all_keys (lua_State *L,
+                            GList **supported_keys,
+                            GList **slow_keys,
+                            GList **resolve_keys,
+                            GrlMediaType *resolve_type,
+                            GHashTable **config_keys)
+{
+  GrlRegistry *registry = NULL;
+  GList *list = NULL;
+  GList *it = NULL;
+  GList *table_list = NULL;
+  GHashTable *htable = NULL;
+  GrlKeyID key_id = GRL_METADATA_KEY_INVALID;
+  const gchar *key_name = NULL;
+
+  GRL_DEBUG ("lua_plugin_source_all_keys");
+
+  /* Keys are in the main table */
+  lua_getglobal (L, LUA_SOURCE_TABLE);
+
+  /* Registry to get metadata keys from key's name */
+  registry = grl_registry_get_default ();
+
+  /* Supported keys */
+  list = NULL;
+  table_list = table_array_to_list (L, LUA_SOURCE_SUPPORTED_KEYS);
+  if (table_list != NULL) {
+    for (it = table_list; it; it = g_list_next (it)) {
+      key_name = it->data;
+      key_id = grl_registry_lookup_metadata_key (registry, key_name);
+
+      if (key_id != GRL_METADATA_KEY_INVALID) {
+        list = g_list_prepend (list, GRLKEYID_TO_POINTER (key_id));
+      }
+    }
+    *supported_keys = list;
+  }
+  g_list_free_full (table_list, g_free);
+
+  /* Slow keys */
+  list = NULL;
+  table_list = table_array_to_list (L, LUA_SOURCE_SLOW_KEYS);
+  if (table_list != NULL) {
+    for (it = table_list; it; it = g_list_next (it)) {
+      key_name = it->data;
+      key_id = grl_registry_lookup_metadata_key (registry, key_name);
+
+      if (key_id != GRL_METADATA_KEY_INVALID) {
+        list = g_list_prepend (list, GRLKEYID_TO_POINTER (key_id));
+      }
+    }
+    *slow_keys = list;
+  }
+  g_list_free_full (table_list, g_free);
+
+  /* Resolve keys - type, required fields */
+  list = NULL;
+  lua_pushstring (L, LUA_SOURCE_RESOLVE_KEYS);
+  lua_gettable (L, -2);
+  if (lua_istable (L, -1)) {
+    GrlMediaType media_type = GRL_MEDIA_TYPE_NONE;
+
+    /* check required type field */
+    lua_pushstring (L, "type");
+    lua_gettable (L, -2);
+    if (lua_isstring (L, -1)) {
+      key_name = lua_tostring (L, -1);
+      if (g_strcmp0 (key_name, "audio") == 0)
+        media_type = GRL_MEDIA_TYPE_AUDIO;
+      else if (g_strcmp0 (key_name, "video") == 0)
+        media_type = GRL_MEDIA_TYPE_VIDEO;
+      else if (g_strcmp0 (key_name, "image") == 0)
+        media_type = GRL_MEDIA_TYPE_IMAGE;
+      else if (g_strcmp0 (key_name, "all") == 0)
+        media_type = GRL_MEDIA_TYPE_ALL;
+    }
+    lua_pop (L, 1);
+
+    /* check required table field */
+    table_list = table_array_to_list (L, LUA_REQUIRED_TABLE);
+    if (table_list != NULL) {
+      for (it = table_list; it; it = g_list_next (it)) {
+        key_name = it->data;
+        key_id = grl_registry_lookup_metadata_key (registry, key_name);
+
+        if (key_id != GRL_METADATA_KEY_INVALID) {
+          list = g_list_prepend (list, GRLKEYID_TO_POINTER (key_id));
+        }
+      }
+      g_list_free_full (table_list, g_free);
+
+      *resolve_type = media_type;
+      *resolve_keys = list;
+    }
+  }
+  lua_pop (L, 1);
+
+  /* Config keys - required and optional fields */
+  lua_pushstring (L, LUA_SOURCE_CONFIG_KEYS);
+  lua_gettable (L, -2);
+  if (lua_istable (L, -1)) {
+    htable = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+    /* check required table field */
+    table_list = table_array_to_list (L, LUA_REQUIRED_TABLE);
+    if (table_list != NULL) {
+      for (it = table_list; it; it = g_list_next (it)) {
+        key_name = it->data;
+        g_hash_table_insert (htable, g_strdup (key_name), g_strdup ("true"));
+      }
+    }
+    g_list_free_full (table_list, g_free);
+
+    /* check optional table field */
+    table_list = table_array_to_list (L, LUA_OPTIONAL_TABLE);
+    if (table_list != NULL) {
+      for (it = table_list; it; it = g_list_next (it)) {
+        key_name = it->data;
+        g_hash_table_insert (htable, g_strdup (key_name), g_strdup ("false"));
+      }
+    }
+    g_list_free_full (table_list, g_free);
+
+    *config_keys = htable;
+  }
+  lua_pop (L, 1);
+
+  return LUA_OK;
+}
+
+/* ================== API Implementation =================================== */
+
+static const GList *
+grl_lua_factory_source_supported_keys (GrlSource *source)
+{
+  GrlLuaFactorySource *lua_source = GRL_LUA_FACTORY_SOURCE (source);
+  return lua_source->priv->supported_keys;
+}
+
+static GrlSupportedOps
+grl_lua_factory_source_supported_operations (GrlSource *source)
+{
+  /* No operation is implemented right now */
+  return 0;
+}
diff --git a/src/lua-factory/grl-lua-factory.h b/src/lua-factory/grl-lua-factory.h
new file mode 100644
index 0000000..f936699
--- /dev/null
+++ b/src/lua-factory/grl-lua-factory.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2013 Victor Toso.
+ *
+ * Contact: Victor Toso <me victortoso 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_LUA_FACTORY_SOURCE_H_
+#define _GRL_LUA_FACTORY_SOURCE_H_
+
+#include <grilo.h>
+
+#define GRL_LUA_FACTORY_SOURCE_TYPE                         (grl_lua_factory_source_get_type())
+#define GRL_LUA_FACTORY_SOURCE(obj)                         (G_TYPE_CHECK_INSTANCE_CAST((obj), 
GRL_LUA_FACTORY_SOURCE_TYPE, GrlLuaFactorySource))
+#define GRL_LUA_FACTORY_SOURCE_CLASS(klass)                 (G_TYPE_CHECK_CLASS_CAST((klass), 
GRL_LUA_FACTORY_SOURCE_TYPE, GrlLuaFactorySourceClass))
+#define GRL_IS_LUA_FACTORY_SOURCE_CLASS(klass)              (G_TYPE_CHECK_CLASS_TYPE((klass), 
GRL_LUA_FACTORY_SOURCE_TYPE))
+#define GRL_LUA_FACTORY_SOURCE_GET_CLASS(obj)               (G_TYPE_INSTANCE_GET_CLASS((obj), 
GRL_LUA_FACTORY_SOURCE_TYPE, GrlLuaFactorySourceClass))
+
+typedef struct _GrlLuaFactorySource GrlLuaFactorySource;
+
+typedef struct _GrlLuaFactorySourcePrivate GrlLuaFactorySourcePrivate;
+
+struct _GrlLuaFactorySource {
+  GrlSource parent;
+  GrlLuaFactorySourcePrivate *priv;
+};
+
+typedef struct _GrlLuaFactorySourceClass GrlLuaFactorySourceClass;
+
+struct _GrlLuaFactorySourceClass {
+  GrlSourceClass parent_class;
+};
+
+GType grl_lua_factory_source_get_type (void);
+
+#endif /* _GRL_LUA_FACTORY_SOURCE_H_ */
diff --git a/src/lua-factory/grl-lua-factory.xml b/src/lua-factory/grl-lua-factory.xml
new file mode 100644
index 0000000..df3dce7
--- /dev/null
+++ b/src/lua-factory/grl-lua-factory.xml
@@ -0,0 +1,10 @@
+<plugin>
+  <info>
+    <name>Lua Source Factory</name>
+    <module>libgrlluafactory</module>
+    <description>A plugin that creates sources from Lua plugins</description>
+    <author>Victor Toso</author>
+    <license>LGPL</license>
+    <site>http://victortoso.com</site>
+  </info>
+</plugin>
diff --git a/src/lua-factory/grl-lua-library.c b/src/lua-factory/grl-lua-library.c
new file mode 100644
index 0000000..7fde7ec
--- /dev/null
+++ b/src/lua-factory/grl-lua-library.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2013 Victor Toso.
+ *
+ * Contact: Victor Toso <me victortoso 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 <net/grl-net.h>
+#include <string.h>
+
+#include "grl-lua-common.h"
+
+#define GRL_LOG_DOMAIN_DEFAULT lua_library_log_domain
+GRL_LOG_DOMAIN_STATIC (lua_library_log_domain);
+
+/* ================== Lua-Library initialization =========================== */
+
+gint
+luaopen_grilo (lua_State *L)
+{
+  static const luaL_Reg library_fn[] = {
+    {NULL, NULL}
+  };
+
+  GRL_LOG_DOMAIN_INIT (lua_library_log_domain, "lua-library");
+
+  GRL_DEBUG ("Loading grilo lua-library");
+  luaL_newlib (L, library_fn);
+
+  return 1;
+}
diff --git a/src/lua-factory/grl-lua-library.h b/src/lua-factory/grl-lua-library.h
new file mode 100644
index 0000000..9fa33df
--- /dev/null
+++ b/src/lua-factory/grl-lua-library.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2013 Victor Toso.
+ *
+ * Contact: Victor Toso <me victortoso 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 <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+#include <grilo.h>
+
+#ifndef _GRL_LUA_LIBRARY_H_
+#define _GRL_LUA_LIBRARY_H_
+
+#define GRILO_LUA_LIBRARY_NAME "grl"
+
+gint luaopen_grilo (lua_State *L);
+
+#endif /* _GRL_LUA_LIBRARY_H_ */
diff --git a/src/lua-factory/sources/Makefile.am b/src/lua-factory/sources/Makefile.am
new file mode 100644
index 0000000..f540d03
--- /dev/null
+++ b/src/lua-factory/sources/Makefile.am
@@ -0,0 +1,16 @@
+#
+# Makefile.am
+#
+# Author: Victor Toso <me victortoso com>
+#
+# Copyright (C) 2013 Victor Toso. All rights reserved.
+
+lua_sources_DATA =
+
+lua_sourcesdir = $(datadir)/$(LUA_FACTORY_SOURCE_LOCATION)
+
+EXTRA_DIST = $(lua_sources_DATA)
+
+MAINTAINERCLEANFILES = *.in *~
+
+DISTCLEANFILES = $(MAINTAINERCLEANFILES)



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