Пример #1
0
static gboolean
grl_metadata_store_source_may_resolve (GrlSource *source,
                                       GrlMedia *media,
                                       GrlKeyID key_id,
                                       GList **missing_keys)
{
  if (!(key_id == GRL_METADATA_KEY_RATING
        || key_id == GRL_METADATA_KEY_PLAY_COUNT
        || key_id == GRL_METADATA_KEY_LAST_PLAYED
        || key_id == GRL_METADATA_KEY_LAST_POSITION
        || key_id == GRL_METADATA_KEY_FAVOURITE))
    return FALSE;


  if (media) {
    if (!(GRL_IS_MEDIA_VIDEO (media) ||
          GRL_IS_MEDIA_AUDIO (media) ||
          key_id == GRL_METADATA_KEY_FAVOURITE))
      /* the keys we handle for now only make sense for audio and video,
         with exception of the 'favourite' key, valid as well for pictures
         and boxes */
      return FALSE;

    if (grl_data_has_key (GRL_DATA (media), GRL_METADATA_KEY_ID))
      return TRUE;
  }

  if (missing_keys)
    *missing_keys = grl_metadata_key_list_new (GRL_METADATA_KEY_ID, NULL);

  return FALSE;
}
Пример #2
0
static void
source_browser (gpointer data,
                gpointer user_data)
{
  GrlSource *source = GRL_SOURCE (data);
  GrlMedia *media = GRL_MEDIA (user_data);
  GList *media_elements;
  GError *error = NULL;
  GList *keys;
  GrlOperationOptions *options = NULL;
  GrlCaps *caps;

  keys = grl_metadata_key_list_new (GRL_METADATA_KEY_TITLE,
                                    GRL_METADATA_KEY_URL,
                                    GRL_METADATA_KEY_MODIFICATION_DATE,
                                    GRL_METADATA_KEY_MIME,
                                    GRL_METADATA_KEY_CHILDCOUNT,
                                    NULL);

  g_debug ("Detected new source available: '%s'",
	   grl_source_get_name (source));

  if (!(grl_source_supported_operations (source) & GRL_OP_BROWSE))
    goto out;

  g_debug ("Browsing source: %s", grl_source_get_name (source));
  /* Here is how you can browse a source, you have to provide:
     1) The source you want to browse contents from.
     2) The container object you want to browse (NULL for the root container)
     3) A list of metadata keys we are interested in.
     4) Options to control certain aspects of the browse operation.
     5) A callback that the framework will invoke for each available result
     6) User data for the callback
     It returns an operation identifier that you can use to match results
     with the corresponding request (we ignore it here) */

  caps = grl_source_get_caps (source, GRL_OP_BROWSE);
  options = grl_operation_options_new (caps);
  grl_operation_options_set_count (options, BROWSE_CHUNK_SIZE);
  grl_operation_options_set_resolution_flags (options, GRL_RESOLVE_IDLE_RELAY);
  media_elements = grl_pls_browse_sync (GRL_SOURCE (source),
                                        media, keys,
                                        options,
                                        NULL,
                                        &error);
  if (!media_elements) {
    g_debug ("No elements found for source: %s!",
             grl_source_get_name (source));
    goto out;
  }

  if (error)
    g_error ("Failed to browse source: %s", error->message);

  g_list_foreach (media_elements, element_browser, source);

out:
  g_list_free (keys);
  g_clear_object (&options);
}
Пример #3
0
static gchar *
get_lyrics (GrlSource *source,
            const gchar *artist,
            const gchar *title)
{
  GList *keys;
  GrlMedia *audio;
  GrlOperationOptions *options;
  GError *error = NULL;
  gchar *lyrics;

  audio = grl_media_audio_new ();
  grl_media_set_artist (audio, artist);
  grl_media_set_title (audio, title);

  keys = grl_metadata_key_list_new (GRL_METADATA_KEY_LYRICS, NULL);
  options = grl_operation_options_new (NULL);
  grl_operation_options_set_resolution_flags (options, GRL_RESOLVE_NORMAL);

  grl_source_resolve_sync (source,
                           GRL_MEDIA (audio),
                           keys,
                           options,
                           &error);
  g_assert_no_error (error);

  lyrics = g_strdup (grl_media_get_lyrics (audio));

  g_list_free (keys);
  g_object_unref (options);
  g_object_unref (audio);
  return lyrics;
}
Пример #4
0
static void
load_plugins (gchar* playlist)
{
  GrlRegistry *registry;
  GrlSource *source;
  GError *error = NULL;
  GList *keys;
  GrlOperationOptions *options;
  GrlCaps *caps;
  GrlMedia* media;
  gboolean pls_media;
  const gchar *mime;

  registry = grl_registry_get_default ();
  grl_registry_load_all_plugins (registry, FALSE, NULL);

  /* Activate plugin */
  if (!grl_registry_activate_plugin_by_id (registry, "grl-filesystem", &error))
    g_error ("Failed to load plugin: %s", error->message);

  source = grl_registry_lookup_source (registry, "grl-filesystem");
  if (!source)
    g_error ("Unable to load grl-filesystem plugin");

  if (!(grl_source_supported_operations (source) & GRL_OP_MEDIA_FROM_URI))
    g_error ("Unable to get media from URI");

  keys = grl_metadata_key_list_new (GRL_METADATA_KEY_TITLE, GRL_METADATA_KEY_URL, GRL_METADATA_KEY_MIME, NULL);
  if (!keys)
    g_error ("Unable to create key list");

  caps = grl_source_get_caps (source, GRL_OP_MEDIA_FROM_URI);
  if (!caps)
    g_error ("Unable to get source caps");

  options = grl_operation_options_new (caps);
  if (!options)
    g_error ("Unable to create operation options");

  media = grl_source_get_media_from_uri_sync (source, playlist, keys, options, &error);
  if (!media)
    g_error ("Unable to get GrlMedia for playlist %s", playlist);

  g_object_unref (options);

  mime = grl_media_get_mime (media);

  pls_media = grl_pls_media_is_playlist (media);

  g_printf("Got Media for %s - mime=%s\n", playlist, mime);
  g_printf("\tgrl_pls_media_is_playlist = %d\n", pls_media);

  if (pls_media) {
    source_browser (source, media);
  }

  g_object_unref (media);
  g_object_unref (source);
}
Пример #5
0
static const GList *
grl_vimeo_source_slow_keys (GrlSource *source)
{
  static GList *keys = NULL;
  if (!keys) {
    keys = grl_metadata_key_list_new (GRL_METADATA_KEY_URL,
                                      GRL_METADATA_KEY_INVALID);
  }
  return keys;
}
Пример #6
0
/**
 * Returns: TRUE if @dependency is in @media, FALSE else.
 * When returning FALSE, if @missing_keys is not NULL it is populated with a
 * list containing @dependency as only element.
 */
static gboolean
has_dependency (GrlMedia *media, GrlKeyID dependency, GList **missing_keys)
{
  if (media && grl_data_has_key (GRL_DATA (media), dependency))
    return TRUE;

  if (missing_keys)
    *missing_keys = grl_metadata_key_list_new (dependency,
                                               NULL);
  return FALSE;
}
Пример #7
0
static void
mex_bliptv_plugin_init (MexBliptvPlugin  *self)
{
  MexBliptvPluginPrivate *priv;
  GrlRegistry *registry;
  GList *sources, *iter;

  priv = self->priv = GET_PRIVATE (self);

  priv->video_models = g_hash_table_new_full (g_direct_hash, g_direct_equal,
                                                   NULL, NULL);

  priv->query_keys = grl_metadata_key_list_new (GRL_METADATA_KEY_ID,
                                                GRL_METADATA_KEY_TITLE,
                                                GRL_METADATA_KEY_MIME,
                                                GRL_METADATA_KEY_URL,
                                                GRL_METADATA_KEY_PUBLICATION_DATE,
                                                NULL);

  priv->video_keys = grl_metadata_key_list_new (GRL_METADATA_KEY_ID,
                                                GRL_METADATA_KEY_DESCRIPTION,
                                                GRL_METADATA_KEY_DURATION,
                                                GRL_METADATA_KEY_THUMBNAIL,
                                                GRL_METADATA_KEY_WIDTH,
                                                GRL_METADATA_KEY_HEIGHT,
                                                NULL);

  priv->manager = mex_model_manager_get_default ();

  registry = grl_registry_get_default ();
  sources = grl_registry_get_sources (registry, FALSE);
  for (iter = sources; iter != NULL; iter = iter->next)
    handle_new_source (self, GRL_SOURCE (iter->data));
  g_list_free (sources);

  g_signal_connect (registry, "source-added",
                    G_CALLBACK (registry_source_added_cb), self);
  g_signal_connect (registry, "source-removed",
                    G_CALLBACK (registry_source_removed_cb), self);
}
Пример #8
0
static const GList *
grl_optical_media_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;
}
Пример #9
0
static const GList *
grl_raitv_source_supported_keys (GrlSource *source)
{
  static GList *keys = NULL;
  if (!keys) {
    keys = grl_metadata_key_list_new (GRL_METADATA_KEY_ID,
                                      GRL_METADATA_KEY_PUBLICATION_DATE,
                                      GRL_METADATA_KEY_TITLE,
                                      GRL_METADATA_KEY_URL,
                                      GRL_METADATA_KEY_THUMBNAIL,
                                      NULL);
  }
  return keys;
}
Пример #10
0
static const GList *
grl_metadata_store_source_writable_keys (GrlSource *source)
{
  static GList *keys = NULL;
  if (!keys) {
    keys = grl_metadata_key_list_new (GRL_METADATA_KEY_RATING,
                                      GRL_METADATA_KEY_PLAY_COUNT,
                                      GRL_METADATA_KEY_LAST_PLAYED,
                                      GRL_METADATA_KEY_LAST_POSITION,
                                      GRL_METADATA_KEY_FAVOURITE,
                                      NULL);
  }
  return keys;
}
Пример #11
0
static gboolean
grl_tmdb_source_may_resolve (GrlSource *source,
                             GrlMedia *media,
                             GrlKeyID key_id,
                             GList **missing_keys)
{
  GrlTmdbSource *self = GRL_TMDB_SOURCE (source);

  if (!g_hash_table_contains (self->priv->supported_keys,
                             GRLKEYID_TO_POINTER (key_id)) &&
      !g_hash_table_contains (self->priv->slow_keys,
                             GRLKEYID_TO_POINTER (key_id)))
    return FALSE;

  /* We can only entertain videos */
  if (media && !grl_media_is_video (media))
    return FALSE;

  /* Caller wants to check what's needed to resolve */
  if (!media) {
    if (missing_keys)
      *missing_keys = grl_metadata_key_list_new (GRL_METADATA_KEY_TITLE, NULL);

    return FALSE;
  }

  /* We can do nothing without a title or the movie-id */
  if (!grl_data_has_key (GRL_DATA (media), GRL_METADATA_KEY_TITLE) &&
          !grl_data_has_key (GRL_DATA (media), GRL_TMDB_METADATA_KEY_TMDB_ID)) {
    if (missing_keys)
      *missing_keys = grl_metadata_key_list_new (GRL_METADATA_KEY_TITLE, NULL);

    return FALSE;
  }

  return TRUE;
}
Пример #12
0
static const GList *
grl_magnatune_source_supported_keys(GrlSource *source)
{
  static GList *keys = NULL;
  if (!keys) {
    keys = grl_metadata_key_list_new(GRL_METADATA_KEY_ID,
                                     GRL_METADATA_KEY_ARTIST,
                                     GRL_METADATA_KEY_ALBUM,
                                     GRL_METADATA_KEY_DURATION,
                                     GRL_METADATA_KEY_TITLE,
                                     GRL_METADATA_KEY_TRACK_NUMBER,
                                     GRL_METADATA_KEY_URL,
                                     GRL_METADATA_KEY_INVALID);
  }
  return keys;
}
Пример #13
0
static const GList *
grl_bookmarks_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_CHILDCOUNT,
                                      GRL_METADATA_KEY_DESCRIPTION,
                                      GRL_METADATA_KEY_THUMBNAIL,
                                      GRL_BOOKMARKS_KEY_BOOKMARK_TIME,
                                      NULL);
  }
  return keys;
}
Пример #14
0
static const GList *
grl_dpap_source_supported_keys (GrlSource *source)
{
  static GList *keys = NULL;

  GRL_DEBUG (__func__);

  if (!keys)
    keys = grl_metadata_key_list_new (GRL_METADATA_KEY_HEIGHT,
                                      GRL_METADATA_KEY_THUMBNAIL,
                                      GRL_METADATA_KEY_WIDTH,
                                      GRL_METADATA_KEY_ID,
                                      GRL_METADATA_KEY_URL,
                                      NULL);

  return keys;
}
static void
set_test_data (GrlSource **source,
               GrlMedia **media,
               GrlOperationOptions **options,
               GList **keys,
               GrlSupportedOps source_op)
{
  *source = test_lua_factory_get_source (FAKE_SOURCE_ID, source_op);
  g_assert_nonnull (*source);

  *media = grl_media_new ();
  grl_data_add_string (GRL_DATA (*media), GRL_METADATA_KEY_URL, LOCAL_CONTENT);

  *keys = grl_metadata_key_list_new (GRL_METADATA_KEY_TITLE, NULL);
  *options = grl_operation_options_new (NULL);
  grl_operation_options_set_resolution_flags (*options, GRL_RESOLVE_NORMAL);
}
Пример #16
0
static GrlCaps *
grl_bookmarks_source_get_caps (GrlSource       *source,
                               GrlSupportedOps  operation)
{
  GList *keys;
  static GrlCaps *caps = NULL;

  if (caps == NULL) {
    caps = grl_caps_new ();
    grl_caps_set_type_filter (caps, GRL_TYPE_FILTER_ALL);
    keys = grl_metadata_key_list_new (GRL_METADATA_KEY_MIME, NULL);
    grl_caps_set_key_filter (caps, keys);
    g_list_free (keys);
  }

  return caps;
}
Пример #17
0
GList *
mex_grilo_program_get_default_keys ()
{
  return grl_metadata_key_list_new (GRL_METADATA_KEY_ID,
                                    GRL_METADATA_KEY_TITLE,
                                    GRL_METADATA_KEY_DESCRIPTION,
                                    GRL_METADATA_KEY_MIME,
                                    GRL_METADATA_KEY_URL,
                                    GRL_METADATA_KEY_THUMBNAIL,
                                    GRL_METADATA_KEY_DATE,
                                    GRL_METADATA_KEY_DURATION,
                                    GRL_METADATA_KEY_WIDTH,
                                    GRL_METADATA_KEY_HEIGHT,
                                    GRL_METADATA_KEY_LAST_POSITION,
                                    GRL_METADATA_KEY_PLAY_COUNT,
                                    GRL_METADATA_KEY_LAST_PLAYED,
                                    NULL);
}
Пример #18
0
static GrlCaps *
grl_metadata_store_source_get_caps (GrlSource *source,
                                    GrlSupportedOps operation)
{
  static GrlCaps *caps = NULL;
  GList * keys;

  if (caps == NULL) {
      caps = grl_caps_new ();
      keys = grl_metadata_key_list_new (GRL_METADATA_KEY_FAVOURITE,
                                        GRL_METADATA_KEY_SOURCE,
                                        GRL_METADATA_KEY_INVALID);
      grl_caps_set_key_filter (caps, keys);
      g_list_free (keys);
      grl_caps_set_type_filter (caps, GRL_TYPE_FILTER_ALL);
  }

  return caps;
}
Пример #19
0
static const GList *
grl_vimeo_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_DESCRIPTION,
				      GRL_METADATA_KEY_URL,
				      GRL_METADATA_KEY_AUTHOR,
				      GRL_METADATA_KEY_PUBLICATION_DATE,
				      GRL_METADATA_KEY_THUMBNAIL,
				      GRL_METADATA_KEY_DURATION,
				      GRL_METADATA_KEY_WIDTH,
				      GRL_METADATA_KEY_HEIGHT,
				      GRL_METADATA_KEY_EXTERNAL_URL,
				      GRL_METADATA_KEY_INVALID);
  }
  return keys;
}
static const GList *
grl_local_metadata_source_supported_keys (GrlSource *source)
{
  static GList *keys = NULL;
  GrlLocalMetadataSourcePriv *priv =
          GRL_LOCAL_METADATA_SOURCE_GET_PRIVATE (source);

  ensure_hash_keyid (priv);
  if (!keys) {
    keys = grl_metadata_key_list_new (GRL_METADATA_KEY_THUMBNAIL,
                                      GRL_METADATA_KEY_TITLE,
                                      GRL_METADATA_KEY_SHOW,
                                      GRL_METADATA_KEY_PUBLICATION_DATE,
                                      GRL_METADATA_KEY_SEASON,
                                      GRL_METADATA_KEY_EPISODE,
                                      GRL_METADATA_KEY_EPISODE_TITLE,
                                      priv->hash_keyid,
                                      NULL);
  }
  return keys;
}
Пример #21
0
static void
mex_grilo_program_get_stream (MexProgram        *program,
                              MexGetStreamReply  reply,
                              gpointer           userdata)
{
  GList *keys;
  GrlMediaSource *source;
  MexGriloProgramClosure *closure;
  MexGriloProgram *self = MEX_GRILO_PROGRAM (program);
  MexGriloProgramPrivate *priv = self->priv;

  closure = g_slice_new0 (MexGriloProgramClosure);
  closure->self = self;
  closure->reply = reply;
  closure->userdata = userdata;

  /* We have to add a reference around ourselves, as you can't cancel
   * the metadata callback and we need to be around when the reply
   * comes.
   */
  g_object_ref (self);

  g_object_get (G_OBJECT (mex_program_get_feed (program)),
                "grilo-source", &source,
                NULL);
  if (GRL_IS_METADATA_SOURCE (source) &&
      grl_metadata_source_supported_operations (
                                                GRL_METADATA_SOURCE (source)) & GRL_OP_METADATA) {
    keys = grl_metadata_key_list_new (GRL_METADATA_KEY_URL);
    grl_media_source_metadata (source,
                               priv->media,
                               keys,
                               GRL_RESOLVE_IDLE_RELAY | GRL_RESOLVE_FULL,
                               mex_grilo_program_get_stream_cb,
                               closure);
    g_list_free (keys);
  }
  else
    g_idle_add (mex_grilo_program_no_get_stream_cb, closure);
}
static gchar *
serialize_media (GrlMedia *media)
{
  static GList *serialize_keys = NULL;
  const gchar *media_id;

  if (!serialize_keys) {
    serialize_keys =
      grl_metadata_key_list_new (GRL_METADATA_KEY_GRILO_MS2_PARENT,
                                 NULL);
  }

  media_id = grl_media_get_id (media);

  /* Check if it is root media */
  if (!media_id) {
    return g_strdup (MS2_ROOT);
  } else {
    return grl_media_serialize_extended (media,
                                         GRL_MEDIA_SERIALIZE_PARTIAL,
                                         serialize_keys);
  }
}
Пример #23
0
static void
mex_library_plugin_init (MexLibraryPlugin *self)
{
    GrlMediaPlugin *plugin;
    GrlPluginRegistry *registry;

    MexLibraryPluginPrivate *priv = self->priv =
                                        LIBRARY_PLUGIN_PRIVATE (self);

    registry = grl_plugin_registry_get_default ();

    plugin = grl_plugin_registry_lookup_source (registry, "grl-filesystem");
    if (plugin)
    {
        GList *query_keys;
        MexFeed *feed;
        GrlMedia *box;
        const gchar *path;
        MexModelInfo *video_info, *photo_info, *music_info;

        query_keys = grl_metadata_key_list_new (GRL_METADATA_KEY_ID,
                                                GRL_METADATA_KEY_TITLE,
                                                GRL_METADATA_KEY_MIME,
                                                GRL_METADATA_KEY_URL,
                                                GRL_METADATA_KEY_DATE,
                                                NULL);

        /* Add the videos model */
        path = g_get_user_special_dir (G_USER_DIRECTORY_VIDEOS);
        box = mex_library_plugin_get_box_for_path (GRL_MEDIA_SOURCE (plugin),
                query_keys, path);

        if (box)
        {
            GList *metadata_keys =
                grl_metadata_key_list_new (GRL_METADATA_KEY_ID,
                                           GRL_METADATA_KEY_DESCRIPTION,
                                           GRL_METADATA_KEY_DURATION,
                                           GRL_METADATA_KEY_THUMBNAIL,
                                           GRL_METADATA_KEY_WIDTH,
                                           GRL_METADATA_KEY_HEIGHT,
                                           NULL);

            feed = mex_grilo_feed_new (GRL_MEDIA_SOURCE (plugin),
                                       query_keys, metadata_keys, box);
            g_object_set (feed, "icon-name", "icon-library",
                          "placeholder-text", "No videos found", NULL);


            mex_grilo_feed_browse (MEX_GRILO_FEED (feed), 0, G_MAXINT);

            video_info = mex_model_info_new_with_sort_funcs (MEX_MODEL (feed),
                         "videos", 0);
            priv->models = g_list_append (priv->models, video_info);
            g_object_unref (feed);
            g_list_free (metadata_keys);
        }

        /* Add the photos model */
        path = g_get_user_special_dir (G_USER_DIRECTORY_PICTURES);
        box = mex_library_plugin_get_box_for_path (GRL_MEDIA_SOURCE (plugin),
                query_keys, path);

        if (box)
        {
            GList *metadata_keys =
                grl_metadata_key_list_new (GRL_METADATA_KEY_ID,
                                           GRL_METADATA_KEY_DESCRIPTION,
                                           GRL_METADATA_KEY_THUMBNAIL,
                                           GRL_METADATA_KEY_WIDTH,
                                           GRL_METADATA_KEY_HEIGHT,
                                           NULL);

            feed = mex_grilo_feed_new (GRL_MEDIA_SOURCE (plugin),
                                       query_keys, metadata_keys, box);
            g_object_set (feed, "icon-name", "icon-library",
                          "placeholder-text", "No pictures found", NULL);

            mex_grilo_feed_browse (MEX_GRILO_FEED (feed), 0, G_MAXINT);

            photo_info = mex_model_info_new_with_sort_funcs (MEX_MODEL (feed),
                         "pictures", 0);
            priv->models = g_list_append (priv->models, photo_info);
            g_object_unref (feed);
            g_list_free (metadata_keys);
        }

        /* Add the music model */
        path = g_get_user_special_dir (G_USER_DIRECTORY_MUSIC);
        box = mex_library_plugin_get_box_for_path (GRL_MEDIA_SOURCE (plugin),
                query_keys, path);

        if (box)
        {
            GList *metadata_keys =
                grl_metadata_key_list_new (GRL_METADATA_KEY_ID,
                                           GRL_METADATA_KEY_DESCRIPTION,
                                           GRL_METADATA_KEY_THUMBNAIL,
                                           GRL_METADATA_KEY_WIDTH,
                                           GRL_METADATA_KEY_HEIGHT,
                                           GRL_METADATA_KEY_ARTIST,
                                           GRL_METADATA_KEY_ALBUM,
                                           NULL);

            feed = mex_grilo_feed_new (GRL_MEDIA_SOURCE (plugin),
                                       query_keys, metadata_keys, box);
            g_object_set (feed, "icon-name", "icon-library",
                          "placeholder-text", "No music found", NULL);

            mex_grilo_feed_browse (MEX_GRILO_FEED (feed), 0, G_MAXINT);

            music_info = mex_model_info_new_with_sort_funcs (MEX_MODEL (feed),
                         "music", 0);
            priv->models = g_list_append (priv->models, music_info);
            g_object_unref (feed);
            g_list_free (metadata_keys);
        }

        g_list_free (query_keys);
    }
    else
        g_warning ("Filesystem plugin not found");
}
Пример #24
0
static void
mex_tracker_plugin_init (MexTrackerPlugin  *self)
{
  MexTrackerPluginPrivate *priv;
  GrlPluginRegistry *registry;
  GList *plugins, *iter;

  priv = self->priv = GET_PRIVATE (self);

  priv->image_models = g_hash_table_new_full (g_direct_hash, g_direct_equal,
                                              g_object_unref, NULL);
  priv->video_models = g_hash_table_new_full (g_direct_hash, g_direct_equal,
                                              g_object_unref, NULL);
  priv->music_models = g_hash_table_new_full (g_direct_hash, g_direct_equal,
                                              g_object_unref, NULL);

  priv->query_video_keys = grl_metadata_key_list_new (GRL_METADATA_KEY_ID,
                                                      GRL_METADATA_KEY_TITLE,
                                                      GRL_METADATA_KEY_MIME,
                                                      GRL_METADATA_KEY_URL,
                                                      GRL_METADATA_KEY_DATE,
                                                      GRL_METADATA_KEY_THUMBNAIL,
                                                      GRL_METADATA_KEY_PLAY_COUNT,
                                                      NULL);

  priv->query_image_keys = grl_metadata_key_list_new (GRL_METADATA_KEY_ID,
                                                      GRL_METADATA_KEY_TITLE,
                                                      GRL_METADATA_KEY_MIME,
                                                      GRL_METADATA_KEY_URL,
                                                      GRL_METADATA_KEY_DATE,
                                                      GRL_METADATA_KEY_THUMBNAIL,
                                                      GRL_METADATA_KEY_PLAY_COUNT,
                                                      NULL);

  priv->query_music_keys = grl_metadata_key_list_new (GRL_METADATA_KEY_ID,
                                                      GRL_METADATA_KEY_TITLE,
                                                      GRL_METADATA_KEY_MIME,
                                                      GRL_METADATA_KEY_URL,
                                                      GRL_METADATA_KEY_DATE,
                                                      GRL_METADATA_KEY_THUMBNAIL,
                                                      GRL_METADATA_KEY_PLAY_COUNT,
                                                      GRL_METADATA_KEY_ALBUM,
                                                      GRL_METADATA_KEY_ARTIST,
                                                      NULL);

  priv->image_keys = grl_metadata_key_list_new (GRL_METADATA_KEY_ID,
                                                GRL_METADATA_KEY_DESCRIPTION,
                                                GRL_METADATA_KEY_THUMBNAIL,
                                                GRL_METADATA_KEY_WIDTH,
                                                GRL_METADATA_KEY_HEIGHT,
                                                GRL_METADATA_KEY_LAST_PLAYED,
                                                GRL_METADATA_KEY_CAMERA_MODEL,
                                                GRL_METADATA_KEY_EXPOSURE_TIME,
                                                GRL_METADATA_KEY_ISO_SPEED,
                                                GRL_METADATA_KEY_FLASH_USED,
                                                GRL_METADATA_KEY_ORIENTATION,
                                                GRL_METADATA_KEY_CREATION_DATE,
                                                NULL);

  priv->video_keys = grl_metadata_key_list_new (GRL_METADATA_KEY_ID,
                                                GRL_METADATA_KEY_DESCRIPTION,
                                                GRL_METADATA_KEY_DURATION,
                                                GRL_METADATA_KEY_THUMBNAIL,
                                                GRL_METADATA_KEY_WIDTH,
                                                GRL_METADATA_KEY_HEIGHT,
                                                GRL_METADATA_KEY_LAST_POSITION,
                                                GRL_METADATA_KEY_LAST_PLAYED,
                                                NULL);

  priv->music_keys = grl_metadata_key_list_new (GRL_METADATA_KEY_ID,
                                                GRL_METADATA_KEY_DURATION,
                                                GRL_METADATA_KEY_THUMBNAIL,
                                                GRL_METADATA_KEY_LAST_POSITION,
                                                GRL_METADATA_KEY_LAST_PLAYED,
                                                NULL);

  priv->manager = mex_model_manager_get_default ();

  /* log domain */
  MEX_LOG_DOMAIN_INIT (tracker_log_domain, "tracker");

  registry = grl_plugin_registry_get_default ();
  plugins = grl_plugin_registry_get_sources (registry, FALSE);
  for (iter = plugins; iter != NULL; iter = iter->next)
    handle_new_source_plugin (self, GRL_MEDIA_PLUGIN (iter->data));
  g_list_free (plugins);

  g_signal_connect (registry, "source-added",
                    G_CALLBACK (registry_source_added_cb), self);
  g_signal_connect (registry, "source-removed",
                    G_CALLBACK (registry_source_removed_cb), self);
}
static gboolean
grl_local_metadata_source_may_resolve (GrlSource *source,
                                       GrlMedia *media,
                                       GrlKeyID key_id,
                                       GList **missing_keys)
{
  GrlLocalMetadataSourcePriv *priv =
    GRL_LOCAL_METADATA_SOURCE_GET_PRIVATE (source);

  if (!media)
    return FALSE;

  if (GRL_IS_MEDIA_AUDIO (media)) {
    gboolean have_artist = FALSE, have_album = FALSE;

    if ((have_artist = grl_data_has_key (GRL_DATA (media),
                                         GRL_METADATA_KEY_ARTIST))
        &&
        (have_album = grl_data_has_key (GRL_DATA (media),
                                        GRL_METADATA_KEY_ALBUM))
        &&
        key_id == GRL_METADATA_KEY_THUMBNAIL) {
      return TRUE;

    } else if (missing_keys) {
      GList *result = NULL;
      if (!have_artist)
        result = g_list_append (result,
                                GRLKEYID_TO_POINTER (GRL_METADATA_KEY_ARTIST));
      if (!have_album)
        result = g_list_append (result,
                                GRLKEYID_TO_POINTER (GRL_METADATA_KEY_ALBUM));

      if (result)
        *missing_keys = result;
    }

    return FALSE;
  }

  if (GRL_IS_MEDIA_IMAGE (media)) {
    if (key_id != GRL_METADATA_KEY_THUMBNAIL)
      return FALSE;
    if (!grl_data_has_key (GRL_DATA (media), GRL_METADATA_KEY_URL))
      goto missing_url;
    if (!has_compatible_media_url (media))
      return FALSE;
    return TRUE;
  }

  if (GRL_IS_MEDIA_VIDEO (media)) {
    switch (key_id) {
    case GRL_METADATA_KEY_TITLE:
    case GRL_METADATA_KEY_SHOW:
    case GRL_METADATA_KEY_PUBLICATION_DATE:
    case GRL_METADATA_KEY_SEASON:
    case GRL_METADATA_KEY_EPISODE:
    case GRL_METADATA_KEY_EPISODE_TITLE:
      if (!priv->guess_video)
        return FALSE;
      if (grl_data_has_key (GRL_DATA (media), GRL_METADATA_KEY_URL) &&
          has_compatible_media_url (media))
        return TRUE;
      if (!grl_data_has_key (GRL_DATA (media), GRL_METADATA_KEY_TITLE))
        goto missing_title;
      return TRUE;
    case GRL_METADATA_KEY_THUMBNAIL:
      if (grl_data_has_key (GRL_DATA (media), GRL_METADATA_KEY_URL) == FALSE)
        goto missing_url;
      return has_compatible_media_url (media);
    default:
      if (key_id == priv->hash_keyid)
        return has_compatible_media_url (media);
    }
  }

missing_title:
  if (missing_keys) {
    if (grl_data_has_key (GRL_DATA (media), GRL_METADATA_KEY_URL) == FALSE)
      *missing_keys = grl_metadata_key_list_new (GRL_METADATA_KEY_TITLE, GRL_METADATA_KEY_URL, NULL);
    else
      *missing_keys = grl_metadata_key_list_new (GRL_METADATA_KEY_TITLE, NULL);
  }
  return FALSE;

missing_url:
  if (missing_keys)
    *missing_keys = grl_metadata_key_list_new (GRL_METADATA_KEY_URL, NULL);

  return FALSE;
}