[libgdata] [youtube] Added a query to retrieve the details of a single video



commit f8490dd614d953e1d28835d97bc70b6746f4e224
Author: Philip Withnall <philip tecnocode co uk>
Date:   Sun Jul 5 22:44:19 2009 +0100

    [youtube] Added a query to retrieve the details of a single video
    
    Added sync and async query functions to retrieve the details of a single video
    based on its video ID. Test cases and documentation are included.

 docs/reference/gdata-sections.txt              |    3 +
 gdata/gdata-private.h                          |    5 +
 gdata/gdata-service.c                          |   99 +++++++++------
 gdata/gdata.symbols                            |    3 +
 gdata/services/youtube/gdata-youtube-service.c |  158 ++++++++++++++++++++++++
 gdata/services/youtube/gdata-youtube-service.h |    7 +
 gdata/tests/youtube.c                          |   52 ++++++++
 7 files changed, 286 insertions(+), 41 deletions(-)
---
diff --git a/docs/reference/gdata-sections.txt b/docs/reference/gdata-sections.txt
index bd23172..505021a 100644
--- a/docs/reference/gdata-sections.txt
+++ b/docs/reference/gdata-sections.txt
@@ -170,6 +170,9 @@ GDataYouTubeStandardFeedType
 gdata_youtube_service_new
 gdata_youtube_service_query_videos
 gdata_youtube_service_query_videos_async
+gdata_youtube_service_query_single_video
+gdata_youtube_service_query_single_video_async
+gdata_youtube_service_query_single_video_finish
 gdata_youtube_service_query_related
 gdata_youtube_service_query_related_async
 gdata_youtube_service_query_standard_feed
diff --git a/gdata/gdata-private.h b/gdata/gdata-private.h
index e92d887..98e0791 100644
--- a/gdata/gdata-private.h
+++ b/gdata/gdata-private.h
@@ -31,6 +31,11 @@ G_BEGIN_DECLS
 #include "gdata-service.h"
 void _gdata_service_set_authenticated (GDataService *self, gboolean authenticated);
 guint _gdata_service_send_message (GDataService *self, SoupMessage *message, GError **error);
+SoupMessage *_gdata_service_query (GDataService *self, const gchar *feed_uri, GDataQuery *query, GCancellable *cancellable,
+				   GDataQueryProgressCallback progress_callback, gpointer progress_user_data, GError **error);
+void _gdata_service_query_async (GDataService *self, const gchar *feed_uri, GDataQuery *query, GType entry_type, GCancellable *cancellable,
+				 GDataQueryProgressCallback progress_callback, gpointer progress_user_data,
+				 GAsyncReadyCallback callback, gpointer user_data, GSimpleAsyncThreadFunc query_thread);
 
 #include "gdata-query.h"
 void _gdata_query_set_next_uri (GDataQuery *self, const gchar *next_uri);
diff --git a/gdata/gdata-service.c b/gdata/gdata-service.c
index ddec843..3beac2c 100644
--- a/gdata/gdata-service.c
+++ b/gdata/gdata-service.c
@@ -942,53 +942,18 @@ gdata_service_query_finish (GDataService *self, GAsyncResult *async_result, GErr
 	g_assert_not_reached ();
 }
 
-/**
- * gdata_service_query:
- * @self: a #GDataService
- * @feed_uri: the feed URI to query, including the host name and protocol
- * @query: a #GDataQuery with the query parameters, or %NULL
- * @entry_type: a #GType for the #GDataEntry<!-- -->s to build from the XML
- * @cancellable: optional #GCancellable object, or %NULL
- * @progress_callback: a #GDataQueryProgressCallback to call when an entry is loaded, or %NULL
- * @progress_user_data: data to pass to the @progress_callback function
- * @error: a #GError, or %NULL
- *
- * Queries the service's @feed_uri feed to build a #GDataFeed.
- *
- * If @cancellable is not %NULL, then the operation can be cancelled by triggering the @cancellable object from another thread.
- * If the operation was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
- *
- * A %GDATA_SERVICE_ERROR_WITH_QUERY will be returned if the server indicates there is a problem with the query, but subclasses may override
- * this and return their own errors. See their documentation for more details.
- *
- * For each entry in the response feed, @progress_callback will be called in the main thread. If there was an error parsing the XML response,
- * a #GDataParserError will be returned.
- *
- * If the query is successful and the feed supports pagination, @query will be updated with the pagination URIs, and the next or previous page
- * can then be loaded by calling gdata_query_next_page() or gdata_query_previous_page() before running the query again.
- *
- * If the #GDataQuery's ETag is set and it finds a match on the server, %FALSE will be returned, but @error will remain unset. Otherwise,
- * @query's ETag will be updated with the ETag from the returned feed, if available.
- *
- * Return value: a #GDataFeed of query results, or %NULL; unref with g_object_unref()
- **/
-GDataFeed *
-gdata_service_query (GDataService *self, const gchar *feed_uri, GDataQuery *query, GType entry_type,
-		     GCancellable *cancellable, GDataQueryProgressCallback progress_callback, gpointer progress_user_data, GError **error)
+/* Does the bulk of the work of gdata_service_query. Split out because certain queries (such as that done by
+ * gdata_youtube_service_query_single_video()) only return a single entry, and thus need special parsing code. */
+SoupMessage *
+_gdata_service_query (GDataService *self, const gchar *feed_uri, GDataQuery *query, GCancellable *cancellable,
+		      GDataQueryProgressCallback progress_callback, gpointer progress_user_data, GError **error)
 {
 	GDataServiceClass *klass;
-	GDataFeed *feed;
 	SoupMessage *message;
-	gchar *query_uri;
 	guint status;
-	GDataLink *link;
-
-	g_return_val_if_fail (GDATA_IS_SERVICE (self), NULL);
-	g_return_val_if_fail (feed_uri != NULL, NULL);
-	g_return_val_if_fail (entry_type != G_TYPE_INVALID, NULL);
 
 	if (query != NULL) {
-		query_uri = gdata_query_get_query_uri (query, feed_uri);
+		gchar *query_uri = gdata_query_get_query_uri (query, feed_uri);
 		message = soup_message_new (SOUP_METHOD_GET, query_uri);
 		g_free (query_uri);
 	} else {
@@ -1026,18 +991,70 @@ gdata_service_query (GDataService *self, const gchar *feed_uri, GDataQuery *quer
 		return NULL;
 	}
 
+	return message;
+}
+
+/**
+ * gdata_service_query:
+ * @self: a #GDataService
+ * @feed_uri: the feed URI to query, including the host name and protocol
+ * @query: a #GDataQuery with the query parameters, or %NULL
+ * @entry_type: a #GType for the #GDataEntry<!-- -->s to build from the XML
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @progress_callback: a #GDataQueryProgressCallback to call when an entry is loaded, or %NULL
+ * @progress_user_data: data to pass to the @progress_callback function
+ * @error: a #GError, or %NULL
+ *
+ * Queries the service's @feed_uri feed to build a #GDataFeed.
+ *
+ * If @cancellable is not %NULL, then the operation can be cancelled by triggering the @cancellable object from another thread.
+ * If the operation was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
+ *
+ * A %GDATA_SERVICE_ERROR_WITH_QUERY will be returned if the server indicates there is a problem with the query, but subclasses may override
+ * this and return their own errors. See their documentation for more details.
+ *
+ * For each entry in the response feed, @progress_callback will be called in the main thread. If there was an error parsing the XML response,
+ * a #GDataParserError will be returned.
+ *
+ * If the query is successful and the feed supports pagination, @query will be updated with the pagination URIs, and the next or previous page
+ * can then be loaded by calling gdata_query_next_page() or gdata_query_previous_page() before running the query again.
+ *
+ * If the #GDataQuery's ETag is set and it finds a match on the server, %FALSE will be returned, but @error will remain unset. Otherwise,
+ * @query's ETag will be updated with the ETag from the returned feed, if available.
+ *
+ * Return value: a #GDataFeed of query results, or %NULL; unref with g_object_unref()
+ **/
+GDataFeed *
+gdata_service_query (GDataService *self, const gchar *feed_uri, GDataQuery *query, GType entry_type,
+		     GCancellable *cancellable, GDataQueryProgressCallback progress_callback, gpointer progress_user_data, GError **error)
+{
+	GDataServiceClass *klass;
+	GDataFeed *feed;
+	SoupMessage *message;
+
+	g_return_val_if_fail (GDATA_IS_SERVICE (self), NULL);
+	g_return_val_if_fail (feed_uri != NULL, NULL);
+	g_return_val_if_fail (entry_type != G_TYPE_INVALID, NULL);
+
+	message = _gdata_service_query (self, feed_uri, query, cancellable, progress_callback, progress_user_data, error);
 	g_assert (message->response_body->data != NULL);
 
+	klass = GDATA_SERVICE_GET_CLASS (self);
 	feed = _gdata_feed_new_from_xml (klass->feed_type, message->response_body->data, message->response_body->length, entry_type,
 					 progress_callback, progress_user_data, error);
 	g_object_unref (message);
 
+	if (feed == NULL)
+		return NULL;
+
 	/* Update the query with the feed's ETag */
 	if (query != NULL && feed != NULL && gdata_feed_get_etag (feed) != NULL)
 		gdata_query_set_etag (query, gdata_feed_get_etag (feed));
 
 	/* Update the query with the next and previous URIs from the feed */
 	if (query != NULL && feed != NULL) {
+		GDataLink *link;
+
 		link = gdata_feed_look_up_link (feed, "next");
 		if (link != NULL)
 			_gdata_query_set_next_uri (query, gdata_link_get_uri (link));
diff --git a/gdata/gdata.symbols b/gdata/gdata.symbols
index c0a0167..50e2e34 100644
--- a/gdata/gdata.symbols
+++ b/gdata/gdata.symbols
@@ -100,6 +100,9 @@ gdata_youtube_service_query_standard_feed
 gdata_youtube_service_query_standard_feed_async
 gdata_youtube_service_query_videos
 gdata_youtube_service_query_videos_async
+gdata_youtube_service_query_single_video
+gdata_youtube_service_query_single_video_async
+gdata_youtube_service_query_single_video_finish
 gdata_youtube_service_query_related
 gdata_youtube_service_query_related_async
 gdata_youtube_service_upload_video
diff --git a/gdata/services/youtube/gdata-youtube-service.c b/gdata/services/youtube/gdata-youtube-service.c
index 855bda4..a685d9f 100644
--- a/gdata/services/youtube/gdata-youtube-service.c
+++ b/gdata/services/youtube/gdata-youtube-service.c
@@ -508,6 +508,164 @@ gdata_youtube_service_query_videos_async (GDataYouTubeService *self, GDataQuery
 }
 
 /**
+ * gdata_youtube_service_query_single_video:
+ * @self: a #GDataYouTubeService
+ * @query: a #GDataQuery with the query parameters, or %NULL
+ * @video_id: the video ID of the desired video
+ * @cancellable: a #GCancellable, or %NULL
+ * @error: a #GError, or %NULL
+ *
+ * Retrieves information about the single video with the given @video_id. @video_id should be as returned by
+ * gdata_youtube_video_get_video_id() or as extracted from a YouTube URI.
+ *
+ * Parameters and errors are as for gdata_service_query().
+ *
+ * Return value: a #GDataYouTubeVideo, or %NULL; unref with g_object_unref()
+ *
+ * Since: 0.4.0
+ **/
+GDataYouTubeVideo *
+gdata_youtube_service_query_single_video (GDataYouTubeService *self, GDataQuery *query, const gchar *video_id,
+					  GCancellable *cancellable, GError **error)
+{
+	gchar *feed_uri;
+	GDataParsable *video;
+	SoupMessage *message;
+
+	g_return_val_if_fail (GDATA_IS_YOUTUBE_SERVICE (self), NULL);
+	g_return_val_if_fail (query == NULL || GDATA_IS_QUERY (query), NULL);
+	g_return_val_if_fail (video_id != NULL, NULL);
+	g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
+
+	/* Query for just the specified video ID */
+	feed_uri = g_strconcat ("http://gdata.youtube.com/feeds/api/videos/";, video_id, NULL);
+	message = _gdata_service_query (GDATA_SERVICE (self), feed_uri, query, cancellable, NULL, NULL, error);
+	g_free (feed_uri);
+
+	if (message == NULL)
+		return NULL;
+
+	g_assert (message->response_body->data != NULL);
+	video = gdata_parsable_new_from_xml (GDATA_TYPE_YOUTUBE_VIDEO, message->response_body->data, message->response_body->length, error);
+	g_object_unref (message);
+
+	return GDATA_YOUTUBE_VIDEO (video);
+}
+
+typedef struct {
+	GDataQuery *query;
+	gchar *video_id;
+} QuerySingleVideoAsyncData;
+
+static void
+query_single_video_async_data_free (QuerySingleVideoAsyncData *data)
+{
+	g_free (data->video_id);
+	g_object_unref (data->query);
+	g_slice_free (QuerySingleVideoAsyncData, data);
+}
+
+static void
+query_single_video_thread (GSimpleAsyncResult *result, GDataService *service, GCancellable *cancellable)
+{
+	GDataYouTubeVideo *video;
+	GError *error = NULL;
+	QuerySingleVideoAsyncData *data = g_simple_async_result_get_op_res_gpointer (result);
+
+	/* Check to see if it's been cancelled already */
+	if (g_cancellable_set_error_if_cancelled (cancellable, &error) == TRUE) {
+		g_simple_async_result_set_from_error (result, error);
+		g_error_free (error);
+		return;
+	}
+
+	/* Execute the query and return */
+	video = gdata_youtube_service_query_single_video (GDATA_YOUTUBE_SERVICE (service), data->query, data->video_id,
+							  cancellable, &error);
+	if (video == NULL) {
+		g_simple_async_result_set_from_error (result, error);
+		g_error_free (error);
+	}
+
+	g_simple_async_result_set_op_res_gpointer (result, video, (GDestroyNotify) g_object_unref);
+}
+
+/**
+ * gdata_youtube_service_query_single_video_async:
+ * @self: a #GDataService
+ * @query: a #GDataQuery with the query parameters, or %NULL
+ * @video_id: the video ID of the desired video
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @callback: a #GAsyncReadyCallback to call when authentication is finished
+ * @user_data: data to pass to the @callback function
+ *
+ * Retrieves information about the single video with the given @video_id. @video_id should be as returned by
+ * gdata_youtube_video_get_video_id() or as extracted from a YouTube URI. @self, @query and @video_id are reffed/copied when this
+ * function is called, so can safely be freed after this function returns.
+ *
+ * For more details, see gdata_youtube_service_query_single_video(), which is the synchronous version of this function.
+ *
+ * When the operation is finished, @callback will be called. You can then call gdata_youtube_service_query_single_video_finish()
+ * to get the results of the operation.
+ *
+ * Since: 0.4.0
+ **/
+void
+gdata_youtube_service_query_single_video_async (GDataYouTubeService *self, GDataQuery *query, const gchar *video_id,
+						GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
+{
+	GSimpleAsyncResult *result;
+	QuerySingleVideoAsyncData *data;
+
+	g_return_if_fail (GDATA_IS_YOUTUBE_SERVICE (self));
+	g_return_if_fail (query == NULL || GDATA_IS_QUERY (query));
+	g_return_if_fail (video_id != NULL);
+	g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+	data = g_slice_new (QuerySingleVideoAsyncData);
+	data->query = (query != NULL) ? g_object_ref (query) : NULL;
+	data->video_id = g_strdup (video_id);
+
+	result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, gdata_youtube_service_query_single_video_async);
+	g_simple_async_result_set_op_res_gpointer (result, data, (GDestroyNotify) query_single_video_async_data_free);
+	g_simple_async_result_run_in_thread (result, (GSimpleAsyncThreadFunc) query_single_video_thread, G_PRIORITY_DEFAULT, cancellable);
+	g_object_unref (result);
+}
+
+/**
+ * gdata_youtube_service_query_single_video_finish:
+ * @self: a #GDataYouTubeService
+ * @async_result: a #GAsyncResult
+ * @error: a #GError, or %NULL
+ *
+ * Finishes an asynchronous query operation for a single video, as started with gdata_youtube_service_query_single_video_async().
+ *
+ * Return value: a #GDataYouTubeVideo, or %NULL; unref with g_object_unref()
+ *
+ * Since: 0.4.0
+ **/
+GDataYouTubeVideo *
+gdata_youtube_service_query_single_video_finish (GDataYouTubeService *self, GAsyncResult *async_result, GError **error)
+{
+	GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (async_result);
+	GDataYouTubeVideo *video;
+
+	g_return_val_if_fail (GDATA_IS_YOUTUBE_SERVICE (self), NULL);
+	g_return_val_if_fail (G_IS_ASYNC_RESULT (async_result), NULL);
+
+	g_warn_if_fail (g_simple_async_result_get_source_tag (result) == gdata_youtube_service_query_single_video_async);
+
+	if (g_simple_async_result_propagate_error (result, error) == TRUE)
+		return NULL;
+
+	video = g_simple_async_result_get_op_res_gpointer (result);
+	if (video != NULL)
+		return g_object_ref (video);
+
+	g_assert_not_reached ();
+}
+
+/**
  * gdata_youtube_service_query_related:
  * @self: a #GDataYouTubeService
  * @video: a #GDataYouTubeVideo for which to find related videos
diff --git a/gdata/services/youtube/gdata-youtube-service.h b/gdata/services/youtube/gdata-youtube-service.h
index e29bc64..4a95f6f 100644
--- a/gdata/services/youtube/gdata-youtube-service.h
+++ b/gdata/services/youtube/gdata-youtube-service.h
@@ -123,6 +123,13 @@ void gdata_youtube_service_query_videos_async (GDataYouTubeService *self, GDataQ
 					       GCancellable *cancellable, GDataQueryProgressCallback progress_callback, gpointer progress_user_data,
 					       GAsyncReadyCallback callback, gpointer user_data);
 
+GDataYouTubeVideo *gdata_youtube_service_query_single_video (GDataYouTubeService *self, GDataQuery *query, const gchar *video_id,
+							     GCancellable *cancellable, GError **error) G_GNUC_WARN_UNUSED_RESULT;
+void gdata_youtube_service_query_single_video_async (GDataYouTubeService *self, GDataQuery *query, const gchar *video_id,
+						     GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
+GDataYouTubeVideo *gdata_youtube_service_query_single_video_finish (GDataYouTubeService *self, GAsyncResult *async_result,
+								    GError **error);
+
 GDataFeed *gdata_youtube_service_query_related (GDataYouTubeService *self, GDataYouTubeVideo *video, GDataQuery *query,
 						GCancellable *cancellable, GDataQueryProgressCallback progress_callback, gpointer progress_user_data,
 						GError **error) G_GNUC_WARN_UNUSED_RESULT;
diff --git a/gdata/tests/youtube.c b/gdata/tests/youtube.c
index 2631142..4d15b71 100644
--- a/gdata/tests/youtube.c
+++ b/gdata/tests/youtube.c
@@ -576,6 +576,55 @@ test_query_uri (GDataService *service)
 	g_object_unref (query);
 }
 
+static void
+test_query_single (GDataService *service)
+{
+	GDataYouTubeVideo *video;
+	GError *error = NULL;
+
+	video = gdata_youtube_service_query_single_video (GDATA_YOUTUBE_SERVICE (service), NULL, "_LeQuMpwbW4", NULL, &error);
+
+	g_assert_no_error (error);
+	g_assert (video != NULL);
+	g_assert (GDATA_IS_YOUTUBE_VIDEO (video));
+	g_assert_cmpstr (gdata_youtube_video_get_video_id (video), ==, "_LeQuMpwbW4");
+	g_assert_cmpstr (gdata_entry_get_id (GDATA_ENTRY (video)), ==, "tag:youtube.com,2008:video:_LeQuMpwbW4");
+	g_clear_error (&error);
+
+	g_object_unref (video);
+}
+
+static void
+test_query_single_async_cb (GDataService *service, GAsyncResult *async_result, GMainLoop *main_loop)
+{
+	GDataYouTubeVideo *video;
+	GError *error = NULL;
+
+	video = gdata_youtube_service_query_single_video_finish (GDATA_YOUTUBE_SERVICE (service), async_result, &error);
+
+	g_assert_no_error (error);
+	g_assert (video != NULL);
+	g_assert (GDATA_IS_YOUTUBE_VIDEO (video));
+	g_assert_cmpstr (gdata_youtube_video_get_video_id (video), ==, "_LeQuMpwbW4");
+	g_assert_cmpstr (gdata_entry_get_id (GDATA_ENTRY (video)), ==, "tag:youtube.com,2008:video:_LeQuMpwbW4");
+	g_clear_error (&error);
+
+	g_main_loop_quit (main_loop);
+	g_object_unref (video);
+}
+
+static void
+test_query_single_async (GDataService *service)
+{
+	GMainLoop *main_loop = g_main_loop_new (NULL, TRUE);
+
+	gdata_youtube_service_query_single_video_async (GDATA_YOUTUBE_SERVICE (service), NULL, "_LeQuMpwbW4", NULL,
+							(GAsyncReadyCallback) test_query_single_async_cb, main_loop);
+
+	g_main_loop_run (main_loop);
+	g_main_loop_unref (main_loop);
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -606,6 +655,9 @@ main (int argc, char *argv[])
 	g_test_add_data_func ("/youtube/parsing/yt:recorded", service, test_parsing_yt_recorded);
 	g_test_add_data_func ("/youtube/query/uri", service, test_query_uri);
 	g_test_add_data_func ("/youtube/query/uri", service, test_query_uri);
+	g_test_add_data_func ("/youtube/query/single", service, test_query_single);
+	if (g_test_slow () == TRUE)
+		g_test_add_data_func ("/youtube/query/single_async", service, test_query_single_async);
 
 	retval = g_test_run ();
 	g_object_unref (service);



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