Exemple #1
0
static void
set_metadata_to_media (GrlMedia           *media,
                       MexContentMetadata  mex_key,
                       const gchar        *value)
{
  int      ival;
  float    fval;
  GrlKeyID grl_key = _get_grl_key_from_mex (mex_key);

  if (!grl_key) {
    g_warning ("No grilo key to handle %s",
               mex_content_metadata_key_to_string (mex_key));
    return;
  }

  switch (G_PARAM_SPEC (grl_key)->value_type) {
  case G_TYPE_STRING:
    grl_data_set_string (GRL_DATA (media), grl_key, value);
    break;

  case G_TYPE_INT:
    ival = atoi (value);
    grl_data_set_int (GRL_DATA (media), grl_key, ival);
    break;

  case G_TYPE_FLOAT:
    fval = atof (value);
    grl_data_set_float (GRL_DATA (media), grl_key, fval);
    break;
  }
}
static void
extract_gibest_hash (GTask        *task,
                     gpointer      source_object,
                     gpointer      task_data,
                     GCancellable *cancellable)
{
  GFile *file = source_object;
  guint64 buffer[2][CHUNK_N_BYTES/8];
  GInputStream *stream = NULL;
  gssize n_bytes, file_size;
  GError *error = NULL;
  guint64 hash = 0;
  gint i;
  char *str;
  ResolveData *resolve_data = task_data;
  GrlLocalMetadataSourcePriv *priv;

  priv = GRL_LOCAL_METADATA_SOURCE_GET_PRIVATE (resolve_data->source);

  stream = G_INPUT_STREAM (g_file_read (file, cancellable, &error));
  if (stream == NULL)
    goto fail;

  /* Extract start/end chunks of the file */
  n_bytes = g_input_stream_read (stream, buffer[0], CHUNK_N_BYTES, cancellable, &error);
  if (n_bytes == -1)
    goto fail;

  if (!g_seekable_seek (G_SEEKABLE (stream), -CHUNK_N_BYTES, G_SEEK_END, cancellable, &error))
    goto fail;

  n_bytes = g_input_stream_read (stream, buffer[1], CHUNK_N_BYTES, cancellable, &error);
  if (n_bytes == -1)
    goto fail;

  for (i = 0; i < G_N_ELEMENTS (buffer[0]); i++)
    hash += buffer[0][i] + buffer[1][i];

  file_size = g_seekable_tell (G_SEEKABLE (stream));

  if (file_size < CHUNK_N_BYTES)
    goto fail;

  /* Include file size */
  hash += file_size;
  g_object_unref (stream);

  str = g_strdup_printf ("%" G_GINT64_FORMAT, hash);
  grl_data_set_string (GRL_DATA (resolve_data->rs->media), priv->hash_keyid, str);
  g_free (str);

  g_task_return_boolean (task, TRUE);
  return;

fail:
  GRL_DEBUG ("Could not get file hash: %s\n", error ? error->message : "Unknown error");
  g_task_return_error (task, error);
  g_clear_object (&stream);
}
Exemple #3
0
void
mex_grilo_set_media_content_metadata (GrlMedia           *media,
                                      MexContentMetadata  mex_key,
                                      const gchar        *value)
{
  int      ival;
  float    fval;
  GrlKeyID grl_key;

  g_return_if_fail (GRL_IS_MEDIA (media));
  g_return_if_fail (mex_key < MEX_CONTENT_METADATA_LAST_ID);

  grl_key = _get_grl_key_from_mex (mex_key);

  if (!grl_key) {
    g_warning ("No grilo key to handle %s",
               mex_content_metadata_key_to_string (mex_key));
    return;
  }

  switch (grl_metadata_key_get_type (grl_key)) {
  case G_TYPE_STRING:
    grl_data_set_string (GRL_DATA (media), grl_key, value);
    break;

  case G_TYPE_INT:
    ival = atoi (value);
    grl_data_set_int (GRL_DATA (media), grl_key, ival);
    break;

  case G_TYPE_FLOAT:
    fval = atof (value);
    grl_data_set_float (GRL_DATA (media), grl_key, fval);
    break;
  }
}
static void
proxy_call_browse_grlnet_async_cb (GObject *source_object,
                                   GAsyncResult *res,
                                   gpointer user_data)
{
  RaitvOperation    *op = (RaitvOperation *) user_data;
  xmlDocPtr           doc = NULL;
  xmlXPathContextPtr  xpath = NULL;
  xmlXPathObjectPtr   obj = NULL;
  gint i, nb_items = 0;
  gsize length;


  GRL_DEBUG ("Response id=%u", op->operation_id);

  GError *wc_error = NULL;
  GError *error = NULL;
  gchar *content = NULL;

  if (g_cancellable_is_cancelled (op->cancellable)) {
    goto finalize;
  }


  if (!grl_net_wc_request_finish (GRL_NET_WC (source_object),
                                  res,
                                  &content,
                                  &length,
                                  &wc_error)) {
    error = g_error_new (GRL_CORE_ERROR,
                         GRL_CORE_ERROR_SEARCH_FAILED,
                         _("Failed to browse: %s"),
                         wc_error->message);

    op->callback (op->source,
                  op->operation_id,
                  NULL,
                  0,
                  op->user_data,
                  error);

    g_error_free (wc_error);
    g_error_free (error);

    return;
  }

  doc = xmlRecoverMemory (content, (gint) length);

  if (!doc) {
    GRL_DEBUG ("Doc failed");
    goto finalize;
  }

  xpath = xmlXPathNewContext (doc);
  if (!xpath) {
    GRL_DEBUG ("Xpath failed");
    goto finalize;
  }

  gchar* xquery=NULL;
  switch (classify_media_id (op->container_id))
    {
    case RAITV_MEDIA_TYPE_POPULAR_THEME:
      xquery = "/CLASSIFICAVISTI/content";
      break;
    case RAITV_MEDIA_TYPE_RECENT_THEME:
      xquery = "/INFORMAZIONICONTENTS/content";
      break;
    default:
      goto finalize;
    }

  obj = xmlXPathEvalExpression ((xmlChar *) xquery, xpath);
  if (obj)
    {
      nb_items = xmlXPathNodeSetGetLength (obj->nodesetval);
      xmlXPathFreeObject (obj);
    }

  if (nb_items < op->count)
    op->count = nb_items;

  op->category_info->count = nb_items;

  for (i = 0; i < nb_items; i++)
    {

      //Skip
      if(op->skip>0) {
        op->skip--;
        continue;
      }

      GrlRaitvSource *source = GRL_RAITV_SOURCE (op->source);
      GList *mapping = source->priv->raitv_browse_mappings;
      GrlMedia *media = grl_media_video_new ();

      while (mapping)
        {
          gchar *str;
          gchar *strvalue;

          RaitvAssoc *assoc = (RaitvAssoc *) mapping->data;
          str = g_strdup_printf ("string(%s[%i]/%s)",
                                 xquery,i + 1, assoc->exp);


          obj = xmlXPathEvalExpression ((xmlChar *) str, xpath);
          if (obj)
            {
              if (obj->stringval && obj->stringval[0] != '\0')
                {
                  strvalue = 	g_strdup((gchar *) obj->stringval);
                  //Sometimes GRL_METADATA_KEY_THUMBNAIL doesn't report complete url
                  if(assoc->grl_key == GRL_METADATA_KEY_THUMBNAIL && !g_str_has_prefix(strvalue,"http://www.rai.tv/")) {
                    strvalue = g_strdup_printf("http://www.rai.tv%s",obj->stringval);
                  }

                  GType _type;
                  _type = grl_metadata_key_get_type (assoc->grl_key);
                  switch (_type)
                    {
                    case G_TYPE_STRING:
                      grl_data_set_string (GRL_DATA (media),
                                           assoc->grl_key,
                                           strvalue);
                      break;

                    case G_TYPE_INT:
                      grl_data_set_int (GRL_DATA (media),
                                        assoc->grl_key,
                                        (gint) atoi (strvalue));
                      break;

                    case G_TYPE_FLOAT:
                      grl_data_set_float (GRL_DATA (media),
                                          assoc->grl_key,
                                          (gfloat) atof (strvalue));
                      break;

                    default:
                      /* G_TYPE_DATE_TIME is not a constant, so this has to be
                       * in "default:" */
                      if (_type == G_TYPE_DATE_TIME) {
                        int year,month,day,hour,minute,seconds;
                        sscanf((const char*)obj->stringval, "%02d/%02d/%04d %02d:%02d:%02d",
                               &day, &month, &year, &hour,&minute, &seconds);
                        GDateTime *date = g_date_time_new_local (year, month, day, hour,minute, seconds);
                        grl_data_set_boxed (GRL_DATA (media),
                                            assoc->grl_key, date);
                        if(date) g_date_time_unref (date);
                      } else {
                        GRL_DEBUG ("\tUnexpected data type: %s",
                                   g_type_name (_type));
                      }
                      break;
                    }
                  g_free (strvalue);
                }
              xmlXPathFreeObject (obj);
            }

          g_free (str);

          mapping = mapping->next;
        }

      op->callback (op->source,
                    op->operation_id,
                    media,
                    --op->count,
                    op->user_data,
                    NULL);

      if (op->count == 0) {
        break;
      }
    }

 finalize:
  g_clear_pointer (&xpath, xmlXPathFreeContext);
  g_clear_pointer (&doc, xmlFreeDoc);

  /* Signal the last element if it was not already signaled */
  if (nb_items == 0) {
    op->callback (op->source,
                  op->operation_id,
                  NULL,
                  0,
                  op->user_data,
                  NULL);
  }
  else {
    //Continue the search
    if(op->count>0) {
      //Skip the ones already read
      op->skip +=  nb_items;
      op->offset +=  nb_items;
      switch (classify_media_id (op->container_id))
		  {
        case RAITV_MEDIA_TYPE_POPULAR_THEME:
          produce_from_popular_theme(op);
          break;
        case RAITV_MEDIA_TYPE_RECENT_THEME:
          produce_from_recent_theme(op);
          break;
        default:
          g_assert_not_reached ();
          break;
		  }

    }
  }

}
static void
proxy_call_search_grlnet_async_cb (GObject *source_object,
                                   GAsyncResult *res,
                                   gpointer user_data)
{
  RaitvOperation    *op = (RaitvOperation *) user_data;
  xmlDocPtr           doc = NULL;
  xmlXPathContextPtr  xpath = NULL;
  xmlXPathObjectPtr   obj = NULL;
  gint i, nb_items = 0;
  gsize length;

  GRL_DEBUG ("Response id=%u", op->operation_id);

  GError *wc_error = NULL;
  GError *error = NULL;
  gchar *content = NULL;
  gboolean g_bVideoNotFound = TRUE;

  if (g_cancellable_is_cancelled (op->cancellable)) {
    goto finalize;
  }

  if (!grl_net_wc_request_finish (GRL_NET_WC (source_object),
                                  res,
                                  &content,
                                  &length,
                                  &wc_error)) {
    error = g_error_new (GRL_CORE_ERROR,
                         GRL_CORE_ERROR_SEARCH_FAILED,
                         _("Failed to search: %s"),
                         wc_error->message);

    op->callback (op->source,
                  op->operation_id,
                  NULL,
                  0,
                  op->user_data,
                  error);

   g_error_free (wc_error);
    g_error_free (error);

    return;
  }

  doc = xmlParseMemory (content, (gint) length);

  if (!doc) {
    GRL_DEBUG ("Doc failed");
    goto finalize;
  }

  xpath = xmlXPathNewContext (doc);
  if (!xpath) {
    GRL_DEBUG ("Xpath failed");
    goto finalize;
  }
  obj = xmlXPathEvalExpression ((xmlChar *) "/GSP/RES/R", xpath);
  if (obj)
    {
      nb_items = xmlXPathNodeSetGetLength (obj->nodesetval);
      xmlXPathFreeObject (obj);
    }

  for (i = 0; i < nb_items; i++)
    {
      //Search only videos
      gchar *str;
      str = g_strdup_printf ("string(/GSP/RES/R[%i]/MT[@N='videourl']/@V)",
                             i + 1);
      obj = xmlXPathEvalExpression ((xmlChar *) str, xpath);
      if (obj->stringval && obj->stringval[0] == '\0')
        continue;
      if(op->skip>0) {
        op->skip--;
        continue;
      }

      GrlRaitvSource *source = GRL_RAITV_SOURCE (op->source);
      GList *mapping = source->priv->raitv_search_mappings;
      GrlMedia *media = grl_media_video_new ();
      g_bVideoNotFound = FALSE;
      GRL_DEBUG ("Mappings count: %d",g_list_length(mapping));
      while (mapping)
        {
          RaitvAssoc *assoc = (RaitvAssoc *) mapping->data;
          str = g_strdup_printf ("string(/GSP/RES/R[%i]/%s)",
                                 i + 1, assoc->exp);

          GRL_DEBUG ("Xquery %s", str);
          gchar *strvalue;

          obj = xmlXPathEvalExpression ((xmlChar *) str, xpath);
          if (obj)
            {
              if (obj->stringval && obj->stringval[0] != '\0')
                {
                  strvalue = 	g_strdup((gchar *) obj->stringval);
                  //Sometimes GRL_METADATA_KEY_THUMBNAIL doesn't report complete url
                  if(assoc->grl_key == GRL_METADATA_KEY_THUMBNAIL && !g_str_has_prefix(strvalue,"http://www.rai.tv")) {
                    strvalue = g_strdup_printf("http://www.rai.tv%s",obj->stringval);
                  }

                  GType _type;
                  GRL_DEBUG ("\t%s -> %s", str, obj->stringval);
                  _type = grl_metadata_key_get_type (assoc->grl_key);
                  switch (_type)
                    {
                    case G_TYPE_STRING:
                      grl_data_set_string (GRL_DATA (media),
                                           assoc->grl_key,
                                           strvalue);
                      break;

                    case G_TYPE_INT:
                      grl_data_set_int (GRL_DATA (media),
                                        assoc->grl_key,
                                        (gint) atoi (strvalue));
                      break;

                    case G_TYPE_FLOAT:
                      grl_data_set_float (GRL_DATA (media),
                                          assoc->grl_key,
                                          (gfloat) atof (strvalue));
                      break;

                    default:
                      /* G_TYPE_DATE_TIME is not a constant, so this has to be
                       * in "default:" */
                      if (_type == G_TYPE_DATE_TIME) {
                        int year,month,day;
                        sscanf((const char*)obj->stringval, "%02d/%02d/%04d", &day, &month, &year);
                        GDateTime *date = g_date_time_new_local (year, month, day, 0, 0, 0);
                        GRL_DEBUG ("Setting %s to %s",
                                   grl_metadata_key_get_name (assoc->grl_key),
                                   g_date_time_format (date, "%F %H:%M:%S"));
                        grl_data_set_boxed (GRL_DATA (media),
                                            assoc->grl_key, date);
                        if(date) g_date_time_unref (date);
                      } else {
                        GRL_DEBUG ("\tUnexpected data type: %s",
                                   g_type_name (_type));
                      }
                      break;
                    }
                  g_free (strvalue);
                }
              xmlXPathFreeObject (obj);
            }

          g_free (str);

          mapping = mapping->next;
        }

      op->callback (op->source,
                    op->operation_id,
                    media,
                    --op->count,
                    op->user_data,
                    NULL);

      if (op->count == 0)
        break;
    }

 finalize:
  g_clear_pointer (&xpath, xmlXPathFreeContext);
  g_clear_pointer (&doc, xmlFreeDoc);

  /* Signal the last element if it was not already signaled */
  if (nb_items == 0 || g_bVideoNotFound) {
    op->callback (op->source,
                  op->operation_id,
                  NULL,
                  0,
                  op->user_data,
                  NULL);
  }
  else {
    //Continue the search
    if(op->count>0) {
		op->offset +=  nb_items;
		g_raitv_videos_search(op);
    }
  }

}
Exemple #6
0
static void
on_search_ready (GObject *source,
                 GAsyncResult *result,
                 gpointer user_data)
{
  ResolveClosure *closure = (ResolveClosure *) user_data;
  GrlTmdbRequest *request = GRL_TMDB_REQUEST (source);
  GValue *value;
  GError *error = NULL;

  GRL_DEBUG ("Initial search ready...");
  if (!grl_tmdb_request_run_finish (GRL_TMDB_REQUEST (source),
                                    result,
                                    &error)) {
    resolve_closure_callback (closure, error);
    resolve_closure_free (closure);
    g_error_free (error);
    return;
  }

  value = grl_tmdb_request_get (request, "$.total_results");
  if (g_value_get_int64 (value) == 0) {
    /* Nothing found */
    resolve_closure_callback (closure, NULL);
    resolve_closure_free (closure);
    g_value_unset (value);
    g_free (value);
    return;
  }
  g_value_unset (value);
  g_free (value);

  value = grl_tmdb_request_get (request, "$.results[0].id");
  if (value == NULL) {
    /* Cannot continue without id */
    error = g_error_new_literal (GRL_CORE_ERROR,
                                 GRL_CORE_ERROR_RESOLVE_FAILED,
                                 _("Remote data does not contain valid identifier"));
    resolve_closure_callback (closure, error);
    resolve_closure_free (closure);
    g_error_free (error);
    return;
  }

  if (SHOULD_RESOLVE (GRL_TMDB_METADATA_KEY_TMDB_ID)) {
    char *tmdb_id = g_strdup_printf ("%" G_GINT64_FORMAT,
                                     g_value_get_int64 (value));
    grl_data_set_string (GRL_DATA (closure->rs->media),
                         GRL_TMDB_METADATA_KEY_TMDB_ID, tmdb_id);
    g_free (tmdb_id);
  }

  closure->id = g_value_get_int64 (value);
  g_value_unset (value);
  g_free (value);

  if (grl_data_get_boolean (GRL_DATA (closure->rs->media), GRL_METADATA_KEY_TITLE_FROM_FILENAME)) {
    value = grl_tmdb_request_get (request, "$.results[0].title");
    if (value) {
      grl_media_set_title (closure->rs->media, g_value_get_string (value));
      grl_data_set_boolean (GRL_DATA (closure->rs->media), GRL_METADATA_KEY_TITLE_FROM_FILENAME, FALSE);
      g_value_unset (value);
      g_free (value);
    }
  }

  if (SHOULD_RESOLVE (GRL_METADATA_KEY_RATING)) {
    value = grl_tmdb_request_get (request, "$.results[0].vote_average");
    if (value != NULL) {
      grl_media_set_rating (closure->rs->media,
                            (float) g_value_get_double (value),
                            10.0f);
      g_value_unset (value);
      g_free (value);
    }
    g_hash_table_remove (closure->keys, GRLKEYID_TO_POINTER (GRL_METADATA_KEY_RATING));
  }

  /* Add thumbnails first, and posters and backdrops later.
   * Posters more likely make a good thumbnail than backdrops.
   */
  if (SHOULD_RESOLVE (GRL_METADATA_KEY_THUMBNAIL)) {
    value = grl_tmdb_request_get (request, "$.results[0].poster_path");
    if (value != NULL) {
        add_image (closure->self, closure->rs->media,
                   GRL_METADATA_KEY_THUMBNAIL,
                   g_value_get_string (value));

        g_value_unset (value);
        g_free (value);
    }
  }

  if (SHOULD_RESOLVE (GRL_TMDB_METADATA_KEY_POSTER)) {
    value = grl_tmdb_request_get (request, "$.results[0].poster_path");
    if (value != NULL) {
        add_image (closure->self, closure->rs->media,
                   GRL_TMDB_METADATA_KEY_POSTER,
                   g_value_get_string (value));

        g_value_unset (value);
        g_free (value);
    }
  }

  if (SHOULD_RESOLVE (GRL_TMDB_METADATA_KEY_BACKDROP)) {
    value = grl_tmdb_request_get (request, "$.results[0].backdrop_path");
    if (value != NULL) {
      add_image (closure->self, closure->rs->media,
                 GRL_TMDB_METADATA_KEY_BACKDROP,
                 g_value_get_string (value));

      g_value_unset (value);
      g_free (value);
    }
  }

  if (SHOULD_RESOLVE (GRL_METADATA_KEY_ORIGINAL_TITLE)) {
    value = grl_tmdb_request_get (request, "$.results[0].original_title");
    if (value != NULL) {
      grl_media_set_original_title (closure->rs->media, g_value_get_string (value));
      g_value_unset (value);
      g_free (value);
    }
    g_hash_table_remove (closure->keys, GRLKEYID_TO_POINTER (GRL_METADATA_KEY_ORIGINAL_TITLE));
  }

  remove_request (closure, request);

  /* Check if we need to do additional requests. */
  if (closure->slow) {
    resolve_slow_details (closure);

    if (run_pending_requests (closure, G_MAXINT) > 0)
      return;
  }

  resolve_closure_callback (closure, NULL);
  resolve_closure_free (closure);
}
Exemple #7
0
static void
on_request_ready (GObject *source,
                  GAsyncResult *result,
                  gpointer user_data) {
  ResolveClosure *closure = (ResolveClosure *) user_data;
  GrlTmdbRequest *request = GRL_TMDB_REQUEST (source);
  const GrlTmdbRequestDetail detail = grl_tmdb_request_get_detail (request);
  GError *error = NULL;
  GList *values, *iter;
  GValue *value;

  if (detail != GRL_TMDB_REQUEST_DETAIL_COUNT) {
    GRL_DEBUG ("Detail request (%s) ready for movie #%" G_GUINT64_FORMAT "...",
               grl_tmdb_request_detail_to_string (detail), closure->id);
  } else {
    GRL_DEBUG ("Detail request (aggregated) ready for movie #%" G_GUINT64_FORMAT "...",
               closure->id);
  }

  if (!grl_tmdb_request_run_finish (GRL_TMDB_REQUEST (source),
                                    result,
                                    &error)) {
    /* Just remove the request and hope the others have some data */
    GRL_WARNING ("Failed to get %s: %s",
                 grl_tmdb_request_get_uri (request),
                 error->message);
    goto out;
  }

  if (SHOULD_RESOLVE (GRL_METADATA_KEY_GENRE)) {
    iter = values = grl_tmdb_request_get_string_list (request, "$.genres..name");
    while (iter != NULL) {
      grl_data_add_string (GRL_DATA (closure->rs->media),
                           GRL_METADATA_KEY_GENRE, iter->data);
      iter = iter->next;
    }
    g_list_free_full (values, g_free);
  }

  if (SHOULD_RESOLVE (GRL_METADATA_KEY_STUDIO)) {
    iter = values = grl_tmdb_request_get_string_list (request, "$.production_companies..name");
    while (iter != NULL) {
      grl_data_add_string (GRL_DATA (closure->rs->media),
                           GRL_METADATA_KEY_STUDIO, iter->data);
      iter = iter->next;
    }
    g_list_free_full (values, g_free);
  }

  if (SHOULD_RESOLVE (GRL_METADATA_KEY_SITE)) {
    value = grl_tmdb_request_get (request, "$.homepage");
    if (value != NULL) {
      grl_media_set_site (closure->rs->media, g_value_get_string (value));
      g_value_unset (value);
      g_free (value);
    }
  }

  if (SHOULD_RESOLVE (GRL_METADATA_KEY_DESCRIPTION)) {
    value = grl_tmdb_request_get (request, "$.overview");
    if (value != NULL) {
      grl_media_set_description (closure->rs->media,
                                 g_value_get_string (value));
      g_value_unset (value);
      g_free (value);
    }
  }

  if (SHOULD_RESOLVE (GRL_TMDB_METADATA_KEY_IMDB_ID)) {
    value = grl_tmdb_request_get (request, "$.imdb_id");
    if (value != NULL) {
      grl_data_set_string (GRL_DATA (closure->rs->media),
                           GRL_TMDB_METADATA_KEY_IMDB_ID,
                           g_value_get_string (value));
      g_value_unset (value);
      g_free (value);
    }
  }

  if (SHOULD_RESOLVE (GRL_METADATA_KEY_RATING)) {
    value = grl_tmdb_request_get (request, "$.vote_average");
    if (value != NULL) {
      grl_media_set_rating (closure->rs->media,
                            (float) g_value_get_double (value),
                            10.0f);
      g_value_unset (value);
      g_free (value);
    }
  }

  if (SHOULD_RESOLVE (GRL_METADATA_KEY_ORIGINAL_TITLE)) {
    value = grl_tmdb_request_get (request, "$.original_title");
    if (value != NULL) {
      grl_media_set_original_title (closure->rs->media, g_value_get_string (value));
      g_value_unset (value);
      g_free (value);
    }
  }

  if (SHOULD_RESOLVE (GRL_METADATA_KEY_TITLE)) {
    value = grl_tmdb_request_get (request, "$.title");
    if (value != NULL) {
      grl_media_set_title (closure->rs->media, g_value_get_string (value));
      grl_data_set_boolean (GRL_DATA (closure->rs->media), GRL_METADATA_KEY_TITLE_FROM_FILENAME, FALSE);
      g_value_unset (value);
      g_free (value);
    }
  }

  if (!closure->slow) {
    /* Add thumbnails first and poster and backdrops later.
     * Posters more likely make a good thumbnail than backdrops.
     */
    if (SHOULD_RESOLVE (GRL_METADATA_KEY_THUMBNAIL)) {
      value = grl_tmdb_request_get (request, "$.poster_path");
      if (value != NULL) {
          add_image (closure->self, closure->rs->media,
                     GRL_METADATA_KEY_THUMBNAIL,
                     g_value_get_string (value));

          g_value_unset (value);
          g_free (value);
      }
    }

    if (SHOULD_RESOLVE (GRL_TMDB_METADATA_KEY_POSTER)) {
      value = grl_tmdb_request_get (request, "$.poster_path");
      if (value != NULL) {
          add_image (closure->self, closure->rs->media,
                     GRL_TMDB_METADATA_KEY_POSTER,
                     g_value_get_string (value));

          g_value_unset (value);
          g_free (value);
      }
    }

    if (SHOULD_RESOLVE (GRL_TMDB_METADATA_KEY_BACKDROP)) {
      value = grl_tmdb_request_get (request, "$.backdrop_path");
      if (value != NULL) {
        add_image (closure->self, closure->rs->media,
                   GRL_TMDB_METADATA_KEY_BACKDROP,
                   g_value_get_string (value));

        g_value_unset (value);
        g_free (value);
      }
    }
  }

  /* Add thumbnails first, and posters and backdrops later.
   * Posters more likely make a good thumbnail than backdrops.
   */
  if (SHOULD_RESOLVE (GRL_METADATA_KEY_THUMBNAIL)) {
    values = grl_tmdb_request_get_string_list_with_filter (request,
                                                           "$.posters",
                                                           neutral_backdrop_filter);
    if (!values)
      values = grl_tmdb_request_get_string_list_with_filter (request,
                                                             "$.images.posters",
                                                             neutral_backdrop_filter);
    iter = values;
    while (iter != NULL) {
      add_image (closure->self, closure->rs->media,
                 GRL_METADATA_KEY_THUMBNAIL,
                 iter->data);

      iter = iter->next;
    }
    g_list_free_full (values, g_free);
  }

  if (SHOULD_RESOLVE (GRL_TMDB_METADATA_KEY_POSTER)) {
    values = grl_tmdb_request_get_string_list_with_filter (request,
                                                           "$.posters",
                                                           neutral_backdrop_filter);
    if (!values)
      values = grl_tmdb_request_get_string_list_with_filter (request,
                                                             "$.images.posters",
                                                             neutral_backdrop_filter);
    iter = values;
    while (iter != NULL) {
      add_image (closure->self, closure->rs->media,
                 GRL_TMDB_METADATA_KEY_POSTER,
                 iter->data);

      iter = iter->next;
    }
    g_list_free_full (values, g_free);
  }

  if (SHOULD_RESOLVE (GRL_TMDB_METADATA_KEY_BACKDROP)) {
    values = grl_tmdb_request_get_string_list_with_filter (request,
                                                           "$.backdrops",
                                                           neutral_backdrop_filter);
    if (!values)
      values = grl_tmdb_request_get_string_list_with_filter (request,
                                                             "$.images.backdrops",
                                                             neutral_backdrop_filter);
    iter = values;
    while (iter != NULL) {
      add_image (closure->self, closure->rs->media,
                 GRL_TMDB_METADATA_KEY_BACKDROP,
                 iter->data);

      iter = iter->next;
    }
    g_list_free_full (values, g_free);
  }

  if (SHOULD_RESOLVE (GRL_METADATA_KEY_KEYWORD)) {
    values = grl_tmdb_request_get_string_list (request,
                                               "$.keywords..name");
    if (!values)
      values = grl_tmdb_request_get_string_list (request,
                                                 "$.keywords.keywords..name");
    iter = values;
    while (iter != NULL) {
      grl_media_add_keyword (closure->rs->media, iter->data);
      iter = iter->next;
    }
    g_list_free_full (values, g_free);
  }

  if (SHOULD_RESOLVE (GRL_METADATA_KEY_PERFORMER)) {
    values = grl_tmdb_request_get_string_list (request, "$.cast..name");
    if (!values)
      values = grl_tmdb_request_get_string_list (request, "$.casts.cast..name");
    iter = values;
    while (iter != NULL) {
      grl_media_add_performer (closure->rs->media, iter->data);
      iter = iter->next;
    }
    g_list_free_full (values, g_free);
  }

  if (SHOULD_RESOLVE (GRL_METADATA_KEY_PRODUCER)) {
    values = grl_tmdb_request_get_string_list_with_filter (request,
                                                           "$.crew[*]",
                                                           producer_filter);
    if (!values)
      values = grl_tmdb_request_get_string_list_with_filter (request,
                                                             "$.casts.crew[*]",
                                                             producer_filter);
    iter = values;
    while (iter != NULL) {
        grl_media_add_producer (closure->rs->media, iter->data);
      iter = iter->next;
    }
    g_list_free_full (values, g_free);
  }

  if (SHOULD_RESOLVE (GRL_METADATA_KEY_DIRECTOR)) {
    values = grl_tmdb_request_get_string_list_with_filter (request,
                                                           "$.crew[*]",
                                                           director_filter);
    if (!values)
      values = grl_tmdb_request_get_string_list_with_filter (request,
                                                             "$.casts.crew[*]",
                                                             director_filter);
    iter = values;
    while (iter != NULL) {
      grl_media_add_director (closure->rs->media, iter->data);
      iter = iter->next;
    }
    g_list_free_full (values, g_free);
  }

  if (SHOULD_RESOLVE (GRL_METADATA_KEY_AUTHOR)) {
    values = grl_tmdb_request_get_string_list_with_filter (request,
                                                           "$.crew[*]",
                                                           writer_filter);
    if (!values)
      values = grl_tmdb_request_get_string_list_with_filter (request,
                                                             "$.casts.crew[*]",
                                                             writer_filter);
    iter = values;
    while (iter != NULL) {
      grl_media_add_author (GRL_MEDIA (closure->rs->media),
                            iter->data);
      iter = iter->next;
    }
    g_list_free_full (values, g_free);
  }

  if (SHOULD_RESOLVE (GRL_METADATA_KEY_REGION) ||
          SHOULD_RESOLVE (GRL_METADATA_KEY_CERTIFICATE) ||
          SHOULD_RESOLVE (GRL_METADATA_KEY_PUBLICATION_DATE)) {
    values = grl_tmdb_request_get_list_with_filter (request,
                                                    "$.countries[*]",
                                                    NULL);
    if (!values)
      values = grl_tmdb_request_get_list_with_filter (request,
                                                      "$.releases.countries[*]",
                                                      NULL);

    for (iter = values; iter != NULL; iter = iter->next) {
      const char *region, *cert, *date;
      GDateTime *pubdate;
      JsonObject *object;

      object = json_node_get_object (iter->data);
      region = json_object_get_string_member (object, "iso_3166_1");
      cert = json_object_get_string_member (object, "certification");
      date = json_object_get_string_member (object, "release_date");
      pubdate = parse_date (date);

      grl_media_add_region_data (closure->rs->media, region, pubdate, cert);

      g_date_time_unref (pubdate);
    }

    g_list_free_full (values, (GDestroyNotify) json_node_free);
  }

out:
  if (error != NULL) {
    g_error_free (error);
  }

  remove_request (closure, request);

  if (g_queue_is_empty (closure->pending_requests)) {
    resolve_closure_callback (closure, NULL);
    resolve_closure_free (closure);
  }
}
static void
call_raw_async_cb (GObject *     source_object,
                   GAsyncResult *res,
                   gpointer      data)
{
  BliptvOperation    *op = (BliptvOperation *) data;
  xmlDocPtr           doc = NULL;
  xmlXPathContextPtr  xpath = NULL;
  xmlXPathObjectPtr   obj = NULL;
  gint i, nb_items = 0;
  gchar *content = NULL;
  gchar *url;
  gsize length;

  GRL_DEBUG ("Response id=%u", op->operation_id);

  if (g_cancellable_is_cancelled (op->cancellable)) {
    goto finalize_send_last;
  }

  if (!grl_net_wc_request_finish (GRL_NET_WC (source_object),
                                  res,
                                  &content,
                                  &length,
                                  NULL)) {
    goto finalize_send_last;
  }

  doc = xmlParseMemory (content, (gint) length);

  if (!doc)
    goto finalize_send_last;

  xpath = xmlXPathNewContext (doc);
  if (!xpath)
    goto finalize_send_last;

  xmlXPathRegisterNs (xpath,
                      (xmlChar *) "blip",
                      (xmlChar *) BLIPTV_BACKEND "/dtd/blip/1.0");
  xmlXPathRegisterNs (xpath,
                      (xmlChar *) "media",
                      (xmlChar *) "http://search.yahoo.com/mrss/");

  obj = xmlXPathEvalExpression ((xmlChar *) "/rss/channel/item", xpath);
  if (obj)
    {
      nb_items = xmlXPathNodeSetGetLength (obj->nodesetval);
      xmlXPathFreeObject (obj);
    }

  if (nb_items == 0) {
    goto finalize_send_last;
  }

  /* Special case: when there are no results in a search, Blip.tv returns an
     element telling precisely that, no results found; so we need to report no
     results too */
  if (nb_items == 1) {
    obj = xmlXPathEvalExpression ((xmlChar *) "string(/rss/channel/item[0]/blip:item_id)", xpath);
    if (!obj || !obj->stringval || obj->stringval[0] == '\0') {
      g_clear_pointer (&obj, (GDestroyNotify) xmlXPathFreeObject);
      nb_items = 0;
      goto finalize_send_last;
    } else {
      xmlXPathFreeObject (obj);
    }
  }

  for (i = op->skip; i < nb_items; i++)
    {
      GList *mapping = bliptv_mappings;
      GrlMedia *media = grl_media_video_new ();

      while (mapping)
        {
          BliptvAssoc *assoc = (BliptvAssoc *) mapping->data;
          gchar *str;

          str = g_strdup_printf ("string(/rss/channel/item[%i]/%s)",
                                 i + 1, assoc->exp);

          obj = xmlXPathEvalExpression ((xmlChar *) str, xpath);
          if (obj)
            {
              if (obj->stringval && obj->stringval[0] != '\0')
                {
                  GType _type;
                  GRL_DEBUG ("\t%s -> %s", str, obj->stringval);
                  _type = grl_metadata_key_get_type (assoc->grl_key);
                  switch (_type)
                    {
                    case G_TYPE_STRING:
                      grl_data_set_string (GRL_DATA (media),
                                           assoc->grl_key,
                                           (gchar *) obj->stringval);
                      break;

                    case G_TYPE_INT:
                      grl_data_set_int (GRL_DATA (media),
                                        assoc->grl_key,
                                        (gint) atoi ((gchar *) obj->stringval));
                      break;

                    case G_TYPE_FLOAT:
                      grl_data_set_float (GRL_DATA (media),
                                          assoc->grl_key,
                                          (gfloat) atof ((gchar *) obj->stringval));
                      break;

                    default:
                      /* G_TYPE_DATE_TIME is not a constant, so this has to be
                       * in "default:" */
                      if (_type == G_TYPE_DATE_TIME) {
                        GDateTime *date =
                            grl_date_time_from_iso8601 ((gchar *) obj->stringval);
                        GRL_DEBUG ("Setting %s to %s",
                                   grl_metadata_key_get_name (assoc->grl_key),
                                   g_date_time_format (date, "%F %H:%M:%S"));
                        grl_data_set_boxed (GRL_DATA (media),
                                            assoc->grl_key, date);
                        g_date_time_unref (date);
                      } else {
                        GRL_DEBUG ("\tUnexpected data type: %s",
                                   g_type_name (_type));
                      }
                      break;
                    }
                }
              xmlXPathFreeObject (obj);
            }

          g_free (str);

          mapping = mapping->next;
        }

      op->callback (op->source,
                    op->operation_id,
                    media,
                    --op->count,
                    op->user_data,
                    NULL);

      if (op->count == 0)
        break;
    }

  if (op->count > 0) {
    /* Request next page */
    op->skip = 0;
    url = g_strdup_printf (op->url, ++op->page);

    GRL_DEBUG ("Operation %d: requesting page %d",
               op->operation_id,
               op->page);

    grl_net_wc_request_async (GRL_BLIPTV_SOURCE (op->source)->priv->wc,
                              url,
                              op->cancellable,
                              call_raw_async_cb,
                              op);
    g_free (url);

    goto finalize_free;
  }

 finalize_send_last:
  /* Signal the last element if it was not already signaled */
  if (nb_items == 0) {
    op->callback (op->source,
                  op->operation_id,
                  NULL,
                  0,
                  op->user_data,
                  NULL);
  }

 finalize_free:
  g_clear_pointer (&xpath, (GDestroyNotify) xmlXPathFreeContext);
  g_clear_pointer (&doc, (GDestroyNotify) xmlFreeDoc);
}
static void
resolve_video (ResolveData         *resolve_data,
               GrlKeyID             key,
               resolution_flags_t   flags)
{
  gchar *title, *showname, *display_name;
  GDateTime *date;
  gint season, episode;
  GrlData *data = GRL_DATA (resolve_data->rs->media);
  resolution_flags_t miss_flags = 0, fill_flags;

  GRL_DEBUG ("%s",__FUNCTION__);

  resolve_data_start_operation (resolve_data, "video");

  if (!(flags & (FLAG_VIDEO_TITLE |
                 FLAG_VIDEO_SHOWNAME |
                 FLAG_VIDEO_DATE |
                 FLAG_VIDEO_SEASON |
                 FLAG_VIDEO_EPISODE |
                 FLAG_VIDEO_EPISODE_TITLE))) {
    resolve_data_finish_operation (resolve_data, "video", NULL);
    return;
  }

  if (grl_data_has_key (data, GRL_METADATA_KEY_TITLE)) {
    if (grl_data_get_boolean (data, GRL_METADATA_KEY_TITLE_FROM_FILENAME)) {
      miss_flags = FLAG_VIDEO_TITLE;
    } else
      miss_flags = 0;
  } else {
    miss_flags = FLAG_VIDEO_TITLE;
  }
  miss_flags |= grl_data_has_key (data, GRL_METADATA_KEY_SHOW) ?
    0 : FLAG_VIDEO_SHOWNAME;
  miss_flags |= grl_data_has_key (data, GRL_METADATA_KEY_PUBLICATION_DATE) ?
    0 : FLAG_VIDEO_DATE;
  miss_flags |= grl_data_has_key (data, GRL_METADATA_KEY_SEASON) ?
    0 : FLAG_VIDEO_SEASON;
  miss_flags |= grl_data_has_key (data, GRL_METADATA_KEY_EPISODE) ?
    0 : FLAG_VIDEO_EPISODE;
  miss_flags |= grl_data_has_key (data, GRL_METADATA_KEY_EPISODE_TITLE) ?
    0 : FLAG_VIDEO_EPISODE_TITLE;

  fill_flags = flags & miss_flags;

  if (!fill_flags) {
    resolve_data_finish_operation (resolve_data, "video", NULL);
    return;
  }

  if (key == GRL_METADATA_KEY_URL) {
    GFile *file;

    file = g_file_new_for_uri (grl_media_get_url (resolve_data->rs->media));
    display_name = g_file_get_basename (file);
    g_object_unref (file);
  } else {
    display_name = g_strdup (grl_media_get_title (resolve_data->rs->media));
  }

  video_guess_values_from_display_name (display_name,
                                        &title, &showname, &date,
                                        &season, &episode);

  g_free (display_name);

  GRL_DEBUG ("\tfound title=%s/showname=%s/year=%i/season=%i/episode=%i",
             title, showname,
             date != NULL ? g_date_time_get_year (date) : 0,
             season, episode);

  if (showname) {
    if (fill_flags & FLAG_VIDEO_SHOWNAME) {
      grl_data_set_string (data, GRL_METADATA_KEY_SHOW, showname);
    }
    g_free (showname);

    if (fill_flags & FLAG_VIDEO_EPISODE_TITLE &&
        title != NULL) {
      grl_data_set_string (data, GRL_METADATA_KEY_EPISODE_TITLE, title);
    }
  } else {
    /* As this is just a guess, don't erase already provided values,
     * unless GRL_METADATA_KEY_TITLE_FROM_FILENAME is set */
    if (grl_data_get_boolean (data, GRL_METADATA_KEY_TITLE_FROM_FILENAME)) {
      if (fill_flags & FLAG_VIDEO_TITLE) {
        grl_data_set_string (data, GRL_METADATA_KEY_TITLE, title);
      }
    }
  }
  g_free (title);

  if (date) {
    if (fill_flags & FLAG_VIDEO_DATE) {
      grl_data_set_boxed (data, GRL_METADATA_KEY_PUBLICATION_DATE, date);
    }
    g_date_time_unref (date);
  }

  if (season && (fill_flags & FLAG_VIDEO_SEASON)) {
    grl_data_set_int (data, GRL_METADATA_KEY_SEASON, season);
  }

  if (episode && (fill_flags & FLAG_VIDEO_EPISODE)) {
    grl_data_set_int (data, GRL_METADATA_KEY_EPISODE, episode);
  }

  resolve_data_finish_operation (resolve_data, "video", NULL);
}