예제 #1
0
파일: grl-net-wc.c 프로젝트: kyoushuu/grilo
static void
grl_net_wc_set_property (GObject *object,
                         guint propid,
                         const GValue *value,
                         GParamSpec *pspec)
{
  GrlNetWc *wc;

  wc = GRL_NET_WC (object);

  switch (propid) {
  case PROP_LOG_LEVEL:
    grl_net_wc_set_log_level (wc, g_value_get_uint (value));
    break;
  case PROP_THROTTLING:
    grl_net_wc_set_throttling (wc, g_value_get_uint (value));
    break;
  case PROP_CACHE:
    grl_net_wc_set_cache (wc, g_value_get_boolean (value));
    break;
  case PROP_CACHE_SIZE:
    grl_net_wc_set_cache_size (wc, g_value_get_uint (value));
    break;
  case PROP_USER_AGENT:
    g_object_set (G_OBJECT (wc->priv->session),
                  "user-agent", g_value_get_string (value),
                  NULL);
    break;
  default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID (wc, propid, pspec);
  }
}
예제 #2
0
파일: grl-net-wc.c 프로젝트: kyoushuu/grilo
static void
grl_net_wc_get_property (GObject *object,
                         guint propid,
                         GValue *value,
                         GParamSpec *pspec)
{
  GrlNetWc *wc;

  wc = GRL_NET_WC (object);

  switch (propid) {
  case PROP_LOG_LEVEL:
    g_value_set_uint (value, wc->priv->log_level);
    break;
  case PROP_THROTTLING:
    g_value_set_uint (value, wc->priv->throttling);
    break;
  case PROP_CACHE:
    g_value_set_boolean(value, cache_is_available (wc));
    break;
  case PROP_CACHE_SIZE:
    g_value_set_uint (value, wc->priv->cache_size);
    break;
  case PROP_USER_AGENT:
    g_object_get_property (G_OBJECT (wc->priv->session), "user_agent", value);
    break;
  default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID (wc, propid, pspec);
  }
}
예제 #3
0
static void
search_videos_complete_cb (GObject *source_object,
                           GAsyncResult *res,
                           gpointer data)
{
  gchar *content = NULL;

  GVimeoVideoSearchCb *search_data = (GVimeoVideoSearchCb *) data;
  grl_net_wc_request_finish (GRL_NET_WC (source_object),
                             res,
                             &content,
                             NULL,
                             NULL);

  process_video_search_result (content, search_data);
}
예제 #4
0
static void
magnatune_check_update_done(GObject *source_object,
                            GAsyncResult *res,
                            gpointer user_data)
{
  gchar *crc_path = NULL;
  gchar *new_crc_path = NULL;
  gchar *new_crc = NULL;
  gchar *old_crc = NULL;
  gsize length = 0;
  gboolean ret = FALSE; 
  GError *err = NULL;

  ret = grl_net_wc_request_finish(GRL_NET_WC(source_object),
                                  res,
                                  &new_crc,
                                  &length,
                                  &err);
  g_object_unref(source_object);

  if (ret == TRUE) {
    new_crc_path = g_build_filename(g_get_user_data_dir(), "grilo-plugins",
                                    GRL_SQL_NEW_CRC, NULL);

    g_file_set_contents(new_crc_path,
                              new_crc,
                              length,
                              &err);

    crc_path = g_build_filename(g_get_user_data_dir(), "grilo-plugins",
                                GRL_SQL_CRC, NULL);

    g_file_get_contents(crc_path,
                        &old_crc,
                        &length,
                        &err);

    if (g_strcmp0(new_crc, old_crc) != 0) {
      magnatune_get_db_async(NULL);
    }

    g_free(new_crc_path);
    g_free(crc_path);
    g_free(old_crc);
  }
}
예제 #5
0
파일: grl-net-wc.c 프로젝트: kyoushuu/grilo
static void
grl_net_wc_finalize (GObject *object)
{
  GrlNetWc *wc;

  wc = GRL_NET_WC (object);
  grl_net_wc_flush_delayed_requests (wc);

  cache_down (wc);
  finalize_requester (wc);
  finalize_mock_requester (wc);

  g_queue_free (wc->priv->pending);
  g_object_unref (wc->priv->session);

  G_OBJECT_CLASS (grl_net_wc_parent_class)->finalize (object);
}
예제 #6
0
static void
read_done_cb (GObject *source_object,
              GAsyncResult *res,
              gpointer user_data)
{
  gchar *content = NULL;
  GError *wc_error = NULL;
  GFlickrData *data = (GFlickrData *) user_data;

  grl_net_wc_request_finish (GRL_NET_WC (source_object),
                         res,
                         &content,
                         NULL,
                         &wc_error);

  data->parse_xml (content, user_data);
}
예제 #7
0
static void
magnatune_get_crc_done(GObject *source_object,
                       GAsyncResult *res,
                       gpointer user_data)
{
  gchar *new_crc_path = NULL;
  gchar *content = NULL;
  gsize length = 0;
  gboolean ret = FALSE; 
  GError *err = NULL;

  GRL_DEBUG("magnatune_get_crc_done");

  ret = grl_net_wc_request_finish(GRL_NET_WC(source_object),
                                  res,
                                  &content,
                                  &length,
                                  &err);
  g_object_unref(source_object);

  if (ret == TRUE) {
    new_crc_path = g_build_filename(g_get_user_data_dir(), "grilo-plugins",
                                    GRL_SQL_NEW_CRC, NULL);

    ret = g_file_set_contents(new_crc_path,
                              content,
                              length,
                              &err);
    if (ret == FALSE) {
      GRL_WARNING("Failed to save crc-file from magnatune to: '%s' - '%s'",
                  new_crc_path, err->message);
    }
    g_free(new_crc_path);

  } else {
    GRL_WARNING("Failed to get crc-file from magnatune: %s", err->message);
  }
}
예제 #8
0
static void
proxy_call_resolve_grlnet_async_cb (GObject *source_object,
                                    GAsyncResult *res,
                                    gpointer user_data)
{
  RaitvOperation     *op = (RaitvOperation *) user_data;
  xmlDocPtr           doc = NULL;
  xmlXPathContextPtr  xpath = NULL;
  GError             *wc_error = NULL;
  GError             *error = NULL;
  gchar              *content = NULL;
  gsize               length;
  gchar              *value;
  gchar              *thumbnail;
  gchar             **tokens;
  GDateTime          *date;

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

  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 resolve: %s"),
                         wc_error->message);

    op->resolveCb (op->source,
                   op->operation_id,
                   op->media,
                   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;
  }

  if (!grl_data_has_key (GRL_DATA (op->media), GRL_METADATA_KEY_URL)) {
    value = eval_xquery ("/html/head/meta[@name='videourl']", xpath);
    if (value) {
      grl_media_set_url (op->media, value);
      g_free (value);
    }
  }

  if (!grl_data_has_key (GRL_DATA (op->media), GRL_METADATA_KEY_TITLE)) {
    value = eval_xquery ("/html/head/meta[@name='title']", xpath);
    if (value) {
      grl_media_set_title (op->media, value);
      g_free (value);
    }
  }

  if (!grl_data_has_key (GRL_DATA (op->media), GRL_METADATA_KEY_PUBLICATION_DATE)) {
    value = eval_xquery ("/html/head/meta[@name='itemDate']", xpath);
    if (value) {
      tokens = g_strsplit (value, "/", -1);
      if (g_strv_length (tokens) >= 3) {
        date = g_date_time_new_local (atoi (tokens[2]), atoi (tokens[1]), atoi (tokens[0]), 0, 0, 0);
        grl_media_set_publication_date (op->media, date);
        g_date_time_unref (date);
      }
      g_strfreev (tokens);
      g_free (value);
    }
  }

  if (!grl_data_has_key (GRL_DATA (op->media), GRL_METADATA_KEY_THUMBNAIL)) {
    value = eval_xquery ("/html/head/meta[@name='vod-image']", xpath);
    if (value) {
      /* Sometimes thumbnail doesn't report a complete url */
      if (value[0] == '/') {
        thumbnail = g_strconcat ("http://www.rai.tv", value, NULL);
        g_free (value);
      } else {
        thumbnail = value;
      }

      grl_media_set_thumbnail (op->media, thumbnail);
      g_free (thumbnail);
    }
  }

 finalize:
  op->resolveCb (op->source,
                 op->operation_id,
                 op->media,
                 op->user_data,
                 NULL);

  g_clear_pointer (&xpath, xmlXPathFreeContext);
  g_clear_pointer (&doc, xmlFreeDoc);
}
예제 #9
0
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;
		  }

    }
  }

}
예제 #10
0
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);
    }
  }

}
예제 #11
0
static void
magnatune_get_db_done(GObject *source_object,
                      GAsyncResult *res,
                      gpointer user_data)
{
  gchar *db_path = NULL;
  gchar *new_db_path = NULL;
  gchar *content = NULL;
  gsize length = 0;
  gboolean ret = FALSE;
  gboolean first_run = FALSE;
  GError *err = NULL;
  GError *err_fn = NULL;
  OperationSpec *os = NULL;
  GrlMagnatuneSource *source = NULL;

  GRL_DEBUG("magnatune_get_db_done");
  os = (OperationSpec *) user_data;
  ret = grl_net_wc_request_finish(GRL_NET_WC(source_object),
                                  res,
                                  &content,
                                  &length,
                                  &err_fn);
  g_object_unref(source_object);

  if (ret == FALSE) {
    err = g_error_new(GRL_CORE_ERROR,
                      GRL_CORE_ERROR_MEDIA_NOT_FOUND,
                      _("Failed to get database from magnatune: %s"),
                      err_fn->message);
    g_error_free(err_fn);

    if (os != NULL)
      os->callback(os->source, os->operation_id, NULL, 0, os->user_data, err);

  } else {
    db_path = g_build_filename(g_get_user_data_dir(), "grilo-plugins",
                               GRL_SQL_DB, NULL);

    /* If this is a first run, new database must be ready to use */
    if (g_file_test(db_path, G_FILE_TEST_EXISTS) == FALSE) {
      new_db_path = db_path;
      first_run = TRUE;
    } else {
      g_free(db_path);
      new_db_path = g_build_filename(g_get_user_data_dir(), "grilo-plugins",
                                     GRL_SQL_NEW_DB, NULL);
    }

    GRL_WARNING("Saving database to path '%s'", new_db_path);
    ret = g_file_set_contents(new_db_path,
                              content,
                              length,
                              &err_fn);

    if (ret == FALSE) {
      err = g_error_new(GRL_CORE_ERROR,
                        GRL_CORE_ERROR_MEDIA_NOT_FOUND,
                        _("Failed to save database from magnatune - '%s'"),
                        err_fn->message);
      g_error_free(err_fn);

      if (os != NULL)
        os->callback(os->source, os->operation_id, NULL, 0, os->user_data, err);

    } else if (first_run == TRUE) {
      source = GRL_MAGNATUNE_SOURCE(os->source);

      if (source->priv->db == NULL) {
        GRL_DEBUG("Opening database connection.");
        if (sqlite3_open(db_path, &source->priv->db) != SQLITE_OK) {
          GRL_WARNING("Failed to open database '%s': %s",
                      db_path,
                      sqlite3_errmsg(source->priv->db));
          sqlite3_close(source->priv->db);
          source->priv->db = NULL;
        }
      }
    }

    g_free(new_db_path);
  }

  if (ret == TRUE && os != NULL) {
    /* execute application's request */
    os->magnatune_cb(os);
  }
}
예제 #12
0
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);
}