[grilo-plugins] [youtube] libgdata: Implemented a first version of search(), browse() and metadata()
- From: Iago Toral Quiroga <itoral src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [grilo-plugins] [youtube] libgdata: Implemented a first version of search(), browse() and metadata()
- Date: Thu, 15 Apr 2010 09:15:53 +0000 (UTC)
commit f7cf8e42ca3484c7ab5df14632cc73f10b769863
Author: Iago Toral Quiroga <itoral igalia com>
Date: Tue Apr 13 18:19:24 2010 +0200
[youtube] libgdata: Implemented a first version of search(), browse()
and metadata()
src/youtube/grl-youtube.c | 621 ++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 582 insertions(+), 39 deletions(-)
---
diff --git a/src/youtube/grl-youtube.c b/src/youtube/grl-youtube.c
index d2f9548..5227893 100644
--- a/src/youtube/grl-youtube.c
+++ b/src/youtube/grl-youtube.c
@@ -49,26 +49,35 @@
/* ----- Feeds categories ---- */
-#define YOUTUBE_VIEWED_ID (YOUTUBE_FEEDS_ID "/most-viewed")
-#define YOUTUBE_VIEWED_NAME "Most viewed"
+#define YOUTUBE_TOP_RATED_ID (YOUTUBE_FEEDS_ID "/0")
+#define YOUTUBE_TOP_RATED_NAME "Top Rated"
-#define YOUTUBE_RATED_ID (YOUTUBE_FEEDS_ID "/most-rated")
-#define YOUTUBE_RATED_NAME "Most rated"
+#define YOUTUBE_TOP_FAVS_ID (YOUTUBE_FEEDS_ID "/1")
+#define YOUTUBE_TOP_FAVS_NAME "Top Favorites"
-#define YOUTUBE_FAVS_ID (YOUTUBE_FEEDS_ID "/top-favs")
-#define YOUTUBE_FAVS_NAME "Top favorites"
+#define YOUTUBE_MOST_VIEWED_ID (YOUTUBE_FEEDS_ID "/2")
+#define YOUTUBE_MOST_VIEWED_NAME "Most Viewed"
-#define YOUTUBE_RECENT_ID (YOUTUBE_FEEDS_ID "/most-recent")
-#define YOUTUBE_RECENT_NAME "Most recent"
+#define YOUTUBE_MOST_POPULAR_ID (YOUTUBE_FEEDS_ID "/3")
+#define YOUTUBE_MOST_POPULAR_NAME "Most Popular"
-#define YOUTUBE_DISCUSSED_ID (YOUTUBE_FEEDS_ID "/most-discussed")
-#define YOUTUBE_DISCUSSED_NAME "Most discussed"
+#define YOUTUBE_MOST_RECENT_ID (YOUTUBE_FEEDS_ID "/4")
+#define YOUTUBE_MOST_RECENT_NAME "Most Recent"
-#define YOUTUBE_FEATURED_ID (YOUTUBE_FEEDS_ID "/featured")
-#define YOUTUBE_FEATURED_NAME "Featured"
+#define YOUTUBE_MOST_DISCUSSED_ID (YOUTUBE_FEEDS_ID "/5")
+#define YOUTUBE_MOST_DISCUSSED_NAME "Most Discussed"
-#define YOUTUBE_MOBILE_ID (YOUTUBE_FEEDS_ID "/mobile")
-#define YOUTUBE_MOBILE_NAME "Watch on mobile"
+#define YOUTUBE_MOST_LINKED_ID (YOUTUBE_FEEDS_ID "/6")
+#define YOUTUBE_MOST_LINKED_NAME "Most Linked"
+
+#define YOUTUBE_MOST_RESPONDED_ID (YOUTUBE_FEEDS_ID "/7")
+#define YOUTUBE_MOST_RESPONDED_NAME "Most Responded"
+
+#define YOUTUBE_FEATURED_ID (YOUTUBE_FEEDS_ID "/8")
+#define YOUTUBE_FEATURED_NAME "Recently Featured"
+
+#define YOUTUBE_MOBILE_ID (YOUTUBE_FEEDS_ID "/9")
+#define YOUTUBE_MOBILE_NAME "Watch On Mobile"
/* --- Other --- */
@@ -76,6 +85,7 @@
#define YOUTUBE_VIDEO_INFO_URL "http://www.youtube.com/get_video_info?video_id=%s"
#define YOUTUBE_VIDEO_URL "http://www.youtube.com/get_video?video_id=%s&t=%s"
+#define YOUTUBE_CATEGORY_URL "http://gdata.youtube.com/feeds/api/videos/-/%s?&start-index=%s&max-results=%s"
#define YOUTUBE_VIDEO_MIME "application/x-shockwave-flash"
#define YOUTUBE_SITE_URL "www.youtube.com"
@@ -108,11 +118,31 @@ typedef struct {
guint count;
GrlMediaSourceResultCb callback;
gpointer user_data;
- const GDataYouTubeService *service;
guint error_code;
GDataQuery *query;
+ gboolean set_childcount;
} OperationSpec;
+typedef struct {
+ gchar *id;
+ gchar *name;
+ gchar *url;
+} CategoryInfo;
+
+typedef struct {
+ AsyncReadCbFunc callback;
+ gchar *url;
+ gpointer user_data;
+} AsyncReadCb;
+
+typedef struct {
+ OperationSpec *os;
+ CategoryInfo *directory;
+ guint index;
+ guint remaining;
+ gboolean set_childcount;
+} ProduceFromDirectoryIdle;
+
typedef enum {
YOUTUBE_MEDIA_TYPE_ROOT,
YOUTUBE_MEDIA_TYPE_FEEDS,
@@ -125,7 +155,6 @@ typedef enum {
#define YOUTUBE_DEVELOPER_KEY "AI39si4EfscPllSfUy1IwexMf__kntTL_G5dfSr2iUEVN45RHGq92Aq0lX25OlnOkG6KTN-4soVAkAf67fWYXuHfVADZYr7S1A"
#define YOUTUBE_CLIENT_ID "test-client"
-
static GrlYoutubeSource *grl_youtube_source_new (void);
gboolean grl_youtube_plugin_init (GrlPluginRegistry *registry,
@@ -139,11 +168,40 @@ static const GList *grl_youtube_source_slow_keys (GrlMetadataSource *source);
static void grl_youtube_source_search (GrlMediaSource *source,
GrlMediaSourceSearchSpec *ss);
+static void grl_youtube_source_browse (GrlMediaSource *source,
+ GrlMediaSourceBrowseSpec *bs);
+
static void grl_youtube_source_metadata (GrlMediaSource *source,
GrlMediaSourceMetadataSpec *ms);
+static void build_categories_directory (void);
+
/* ==================== Global Data ================= */
+guint root_dir_size = 2;
+CategoryInfo root_dir[] = {
+ {YOUTUBE_FEEDS_ID, YOUTUBE_FEEDS_NAME, NULL},
+ {YOUTUBE_CATEGORIES_ID, YOUTUBE_CATEGORIES_NAME, YOUTUBE_CATEGORIES_URL},
+ {NULL, NULL, NULL}
+};
+
+guint feeds_dir_size = 10;
+CategoryInfo feeds_dir[] = {
+ {YOUTUBE_TOP_RATED_ID, YOUTUBE_TOP_RATED_NAME, NULL},
+ {YOUTUBE_TOP_FAVS_ID, YOUTUBE_TOP_FAVS_NAME, NULL},
+ {YOUTUBE_MOST_VIEWED_ID, YOUTUBE_MOST_VIEWED_NAME, NULL},
+ {YOUTUBE_MOST_POPULAR_ID, YOUTUBE_MOST_POPULAR_NAME, NULL},
+ {YOUTUBE_MOST_RECENT_ID, YOUTUBE_MOST_RECENT_NAME, NULL},
+ {YOUTUBE_MOST_DISCUSSED_ID, YOUTUBE_MOST_DISCUSSED_NAME, NULL},
+ {YOUTUBE_MOST_LINKED_ID, YOUTUBE_MOST_LINKED_NAME, NULL},
+ {YOUTUBE_MOST_RESPONDED_ID, YOUTUBE_MOST_RESPONDED_NAME, NULL},
+ {YOUTUBE_FEATURED_ID, YOUTUBE_FEATURED_NAME, NULL},
+ {YOUTUBE_MOBILE_ID, YOUTUBE_MOBILE_NAME, NULL},
+ {NULL, NULL, NULL}
+};
+
+guint categories_dir_size = 0;
+CategoryInfo *categories_dir = NULL;
/* =================== Youtube Plugin =============== */
@@ -152,7 +210,7 @@ grl_youtube_plugin_init (GrlPluginRegistry *registry,
const GrlPluginInfo *plugin,
GList *config)
{
- g_debug ("youtube_plugin_init\n");
+ g_debug ("youtube_plugin_init");
/* libgdata needs this */
if (!g_thread_supported()) {
@@ -211,9 +269,14 @@ grl_youtube_source_class_init (GrlYoutubeSourceClass * klass)
GrlMediaSourceClass *source_class = GRL_MEDIA_SOURCE_CLASS (klass);
GrlMetadataSourceClass *metadata_class = GRL_METADATA_SOURCE_CLASS (klass);
source_class->search = grl_youtube_source_search;
+ source_class->browse = grl_youtube_source_browse;
source_class->metadata = grl_youtube_source_metadata;
metadata_class->supported_keys = grl_youtube_source_supported_keys;
metadata_class->slow_keys = grl_youtube_source_slow_keys;
+
+ if (!categories_dir) {
+ build_categories_directory ();
+ }
}
static void
@@ -256,6 +319,52 @@ read_url (const gchar *url)
}
}
+static void
+read_done_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ AsyncReadCb *arc = (AsyncReadCb *) user_data;
+ GError *vfs_error = NULL;
+ gchar *content = NULL;
+
+ g_file_load_contents_finish (G_FILE (source_object),
+ res,
+ &content,
+ NULL,
+ NULL,
+ &vfs_error);
+ g_object_unref (source_object);
+ if (vfs_error) {
+ g_warning ("Failed to open '%s': %s", arc->url, vfs_error->message);
+ } else {
+ arc->callback (content, arc->user_data);
+ }
+ g_free (arc->url);
+ g_free (arc);
+}
+
+static void
+read_url_async (const gchar *url,
+ AsyncReadCbFunc callback,
+ gpointer user_data)
+{
+ GVfs *vfs;
+ GFile *uri;
+ AsyncReadCb *arc;
+
+ vfs = g_vfs_get_default ();
+
+ g_debug ("Opening async '%s'", url);
+
+ arc = g_new0 (AsyncReadCb, 1);
+ arc->url = g_strdup (url);
+ arc->callback = callback;
+ arc->user_data = user_data;
+ uri = g_vfs_get_file_for_uri (vfs, url);
+ g_file_load_contents_async (uri, NULL, read_done_cb, arc);
+}
+
static gchar *
get_video_url (const gchar *id)
{
@@ -389,35 +498,128 @@ build_media_from_entry (GrlMedia *content,
}
static void
+parse_categories (xmlDocPtr doc, xmlNodePtr node)
+{
+ g_debug ("parse_categories");
+
+ guint total = 0;
+ GList *all = NULL, *iter;
+ CategoryInfo *cat_info;
+ gchar *id;
+ guint index = 0;
+
+ while (node) {
+ cat_info = g_new (CategoryInfo, 1);
+ id = (gchar *) xmlGetProp (node, (xmlChar *) "term");
+ cat_info->id = g_strconcat (YOUTUBE_CATEGORIES_ID, "/", id, NULL);
+ cat_info->name = (gchar *) xmlGetProp (node, (xmlChar *) "label");
+ cat_info->url = g_strdup_printf (YOUTUBE_CATEGORY_URL,
+ id, "%d", "%d");
+ all = g_list_prepend (all, cat_info);
+ g_free (id);
+ node = node->next;
+ total++;
+ g_debug (" Found category: '%d - %s'", index++, cat_info->name);
+ }
+
+ if (all) {
+ categories_dir_size = total;
+ categories_dir = g_new0 (CategoryInfo, total + 1);
+ iter = all;
+ do {
+ cat_info = (CategoryInfo *) iter->data;
+ categories_dir[total - 1].id = cat_info->id ;
+ categories_dir[total - 1].name = cat_info->name;
+ categories_dir[total - 1].url = cat_info->url;
+ total--;
+ g_free (cat_info);
+ iter = g_list_next (iter);
+ } while (iter);
+ g_list_free (all);
+ }
+}
+
+static void
+build_categories_directory_read_cb (gchar *xmldata, gpointer user_data)
+{
+ xmlDocPtr doc;
+ xmlNodePtr node;
+
+ if (!xmldata) {
+ g_critical ("Failed to build category directory (1)");
+ return;
+ }
+
+ doc = xmlRecoverDoc ((xmlChar *) xmldata);
+ if (!doc) {
+ g_critical ("Failed to build category directory (2)");
+ goto free_resources;
+ }
+
+ node = xmlDocGetRootElement (doc);
+ if (!node) {
+ g_critical ("Failed to build category directory (3)");
+ goto free_resources;
+ }
+
+ if (xmlStrcmp (node->name, (const xmlChar *) "categories")) {
+ g_critical ("Failed to build category directory (4)");
+ goto free_resources;
+ }
+
+ node = node->xmlChildrenNode;
+ if (!node) {
+ g_critical ("Failed to build category directory (5)");
+ goto free_resources;
+ }
+
+ parse_categories (doc, node);
+
+ free_resources:
+ xmlFreeDoc (doc);
+ return;
+}
+
+static void
+build_categories_directory (void)
+{
+ read_url_async (YOUTUBE_CATEGORIES_URL,
+ build_categories_directory_read_cb,
+ NULL);
+}
+
+static void
process_feed (GDataFeed *feed, OperationSpec *os)
{
GList *entries;
+ guint count;
/* Send results to client */
entries = gdata_feed_get_entries (feed);
- while (entries) {
- GrlMedia *media =
- build_media_from_entry (NULL, GDATA_ENTRY (entries->data), os->keys);
- os->callback (os->source,
- os->operation_id,
- media,
- --(os->count),
- os->user_data,
- NULL);
- entries = g_list_next (entries);
- }
-
- /* Send last result if not yet sent */
- if (os->count > 0) {
+ count = g_list_length (entries);
+ if (!entries) {
os->callback (os->source,
os->operation_id,
NULL,
0,
os->user_data,
- NULL);
+ NULL);
+ } else {
+ while (entries) {
+ GrlMedia *media =
+ build_media_from_entry (NULL, GDATA_ENTRY (entries->data), os->keys);
+ os->callback (os->source,
+ os->operation_id,
+ media,
+ --count,
+ os->user_data,
+ NULL);
+ entries = g_list_next (entries);
+ }
}
free_operation_spec (os);
+ g_object_unref (feed);
}
static void
@@ -435,12 +637,16 @@ metadata_cb (GObject *object,
g_debug ("metadata_cb");
GError *error = NULL;
- GrlYoutubeSource *source = GRL_YOUTUBE_SOURCE (ms->source);
+ GrlYoutubeSource *source;
+ GDataYouTubeVideo *video;
+ GDataYouTubeService *service;
- GDataYouTubeVideo *video =
- gdata_youtube_service_query_single_video_finish (GDATA_YOUTUBE_SERVICE (source->service),
- result,
- &error);
+ source = GRL_YOUTUBE_SOURCE (ms->source);
+ service = GDATA_YOUTUBE_SERVICE (source->service);
+
+ video = gdata_youtube_service_query_single_video_finish (service,
+ result,
+ &error);
if (error) {
error->code = GRL_ERROR_METADATA_FAILED;
ms->callback (ms->source, ms->media, ms->user_data, error);
@@ -483,6 +689,298 @@ search_cb (GObject *object, GAsyncResult *result, OperationSpec *os)
}
}
+static gboolean
+is_category_container (const gchar *container_id)
+{
+ return g_str_has_prefix (container_id, YOUTUBE_CATEGORIES_ID "/");
+}
+
+static gboolean
+is_feeds_container (const gchar *container_id)
+{
+ return g_str_has_prefix (container_id, YOUTUBE_FEEDS_ID "/");
+}
+
+static YoutubeMediaType
+classify_media_id (const gchar *media_id)
+{
+ if (!media_id) {
+ return YOUTUBE_MEDIA_TYPE_ROOT;
+ } else if (!strcmp (media_id, YOUTUBE_FEEDS_ID)) {
+ return YOUTUBE_MEDIA_TYPE_FEEDS;
+ } else if (!strcmp (media_id, YOUTUBE_CATEGORIES_ID)) {
+ return YOUTUBE_MEDIA_TYPE_CATEGORIES;
+ } else if (is_category_container (media_id)) {
+ return YOUTUBE_MEDIA_TYPE_CATEGORY;
+ } else if (is_feeds_container (media_id)) {
+ return YOUTUBE_MEDIA_TYPE_FEED;
+ } else {
+ return YOUTUBE_MEDIA_TYPE_VIDEO;
+ }
+}
+
+static gint
+get_feed_type_from_id (const gchar *feed_id)
+{
+ gchar *tmp;
+ gchar *test;
+ gint feed_type;
+
+ tmp = g_strrstr (feed_id, "/");
+ if (!tmp) {
+ return -1;
+ }
+ tmp++;
+
+ feed_type = strtol (tmp, &test, 10);
+ if (*test != '\0') {
+ return -1;
+ }
+
+ return feed_type;
+}
+
+static const gchar *
+get_category_name_from_id (const gchar *category_id)
+{
+ gchar *cat;
+ cat = g_strrstr (category_id, "/");
+ if (!cat) {
+ return NULL;
+ }
+ return ++cat;
+}
+
+static void
+set_category_childcount (GDataYouTubeService *service,
+ GrlMediaBox *content,
+ CategoryInfo *dir,
+ guint index)
+{
+ gint childcount;
+ gboolean set_childcount = TRUE;
+
+ if (dir == NULL) {
+ /* Special case: we want childcount of root category */
+ childcount = root_dir_size;
+ } else if (!strcmp (dir[index].id, YOUTUBE_FEEDS_ID)) {
+ childcount = feeds_dir_size;
+ } else if (!strcmp (dir[index].id, YOUTUBE_CATEGORIES_ID)) {
+ childcount = categories_dir_size;
+ } else {
+ guint feed_type;
+ GDataFeed *feed;
+ GDataQuery *query = gdata_query_new_with_limits (NULL, 0, 1);
+ feed_type = get_feed_type_from_id (grl_media_get_id (GRL_MEDIA (content)));
+ if (feed_type >= 0) {
+ feed = gdata_youtube_service_query_standard_feed (service,
+ feed_type,
+ query,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ }
+
+ if (feed) {
+ childcount = gdata_feed_get_total_results (feed);
+ g_object_unref (feed);
+ } else {
+ g_warning ("Failed to compute childcount for '%s'",
+ grl_media_get_id (GRL_MEDIA (content)));
+ set_childcount = FALSE;
+ }
+
+ g_object_unref (query);
+ }
+
+ if (set_childcount) {
+ grl_media_box_set_childcount (content, childcount);
+ }
+}
+
+static GrlMedia *
+produce_container_from_directory (GDataYouTubeService *service,
+ GrlMedia *media,
+ CategoryInfo *dir,
+ guint index,
+ gboolean set_childcount)
+{
+ GrlMedia *content;
+
+ if (!media) {
+ /* Create mode */
+ content = grl_media_box_new ();
+ } else {
+ /* Update mode */
+ content = media;
+ }
+
+ if (!dir) {
+ grl_media_set_id (content, NULL);
+ grl_media_set_title (content, YOUTUBE_ROOT_NAME);
+ } else {
+ grl_media_set_id (content, dir[index].id);
+ grl_media_set_title (content, dir[index].name);
+ }
+ grl_media_set_site (content, YOUTUBE_SITE_URL);
+ if (set_childcount) {
+ set_category_childcount (service, GRL_MEDIA_BOX (content), dir, index);
+ }
+
+ return content;
+}
+
+static gboolean
+produce_from_directory_idle (gpointer user_data)
+{
+ GrlMedia *content;
+ ProduceFromDirectoryIdle *pfdi = (ProduceFromDirectoryIdle *) user_data;
+
+ content =
+ produce_container_from_directory (GRL_YOUTUBE_SOURCE (pfdi->os->source)->service,
+ NULL,
+ pfdi->directory,
+ pfdi->index,
+ pfdi->set_childcount);
+ pfdi->remaining--;
+ pfdi->index++;
+
+ pfdi->os->callback (pfdi->os->source,
+ pfdi->os->operation_id,
+ content,
+ pfdi->remaining,
+ pfdi->os->user_data,
+ NULL);
+
+ if (pfdi->remaining == 0) {
+ free_operation_spec (pfdi->os);
+ g_free (pfdi);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+produce_from_directory (CategoryInfo *dir, guint dir_size, OperationSpec *os)
+{
+ g_debug ("produce_from_directory");
+
+ guint index, remaining;
+ gboolean set_childcount;
+ YoutubeMediaType media_type;
+
+ if (os->skip >= dir_size) {
+ /* No results */
+ os->callback (os->source,
+ os->operation_id,
+ NULL,
+ 0,
+ os->user_data,
+ NULL);
+ free_operation_spec (os);
+ } else {
+ /* Do not compute childcount when it is expensive and user requested
+ GRL_RESOLVE_FAST_ONLY */
+ media_type = classify_media_id (os->container_id);
+ if ((os->flags & GRL_RESOLVE_FAST_ONLY) &&
+ (media_type == YOUTUBE_MEDIA_TYPE_CATEGORIES ||
+ media_type == YOUTUBE_MEDIA_TYPE_FEEDS)) {
+ set_childcount = FALSE;
+ } else {
+ set_childcount =
+ (g_list_find (os->keys,
+ GRLKEYID_TO_POINTER (GRL_METADATA_KEY_CHILDCOUNT)) != NULL);
+ }
+ index = os->skip;
+ remaining = MIN (dir_size - os->skip, os->count);
+
+ /* We use the idle loop because computing the childcount is blocking
+ and it may be called for every entry in the directory */
+ ProduceFromDirectoryIdle *pfdi = g_new0 (ProduceFromDirectoryIdle, 1);
+ pfdi->os = os;
+ pfdi->directory = dir;
+ pfdi->index = index;
+ pfdi->remaining = remaining;
+ pfdi->set_childcount = set_childcount;
+ g_idle_add (produce_from_directory_idle, pfdi);
+ }
+}
+
+static void
+produce_from_feed (OperationSpec *os)
+{
+ GError *error = NULL;
+ gint feed_type;
+ GDataQuery *query;
+ GDataYouTubeService *service;
+
+ feed_type = get_feed_type_from_id (os->container_id);
+
+ if (feed_type < 0) {
+ error = g_error_new (GRL_ERROR,
+ GRL_ERROR_BROWSE_FAILED,
+ "Invalid feed id: %s", os->container_id);
+ os->callback (os->source,
+ os->operation_id,
+ NULL,
+ 0,
+ os->user_data,
+ error);
+ g_error_free (error);
+ return;
+ }
+
+ service = GDATA_YOUTUBE_SERVICE (GRL_YOUTUBE_SOURCE (os->source)->service);
+ query = gdata_query_new_with_limits (NULL , os->skip, os->count);
+ gdata_youtube_service_query_standard_feed_async (service,
+ feed_type,
+ query,
+ NULL,
+ NULL,
+ NULL,
+ (GAsyncReadyCallback) search_cb,
+ os);
+}
+
+static void
+produce_from_category (OperationSpec *os)
+{
+ GError *error = NULL;
+ GDataQuery *query;
+ GDataYouTubeService *service;
+ const gchar *category_name;
+
+ category_name = get_category_name_from_id (os->container_id);
+
+ if (!category_name) {
+ error = g_error_new (GRL_ERROR,
+ GRL_ERROR_BROWSE_FAILED,
+ "Invalid category id: %s", os->container_id);
+ os->callback (os->source,
+ os->operation_id,
+ NULL,
+ 0,
+ os->user_data,
+ error);
+ g_error_free (error);
+ return;
+ }
+
+ service = GDATA_YOUTUBE_SERVICE (GRL_YOUTUBE_SOURCE (os->source)->service);
+ query = gdata_query_new_with_limits (NULL , os->skip, os->count);
+ os->query = query;
+ gdata_query_set_categories (query, category_name);
+ gdata_youtube_service_query_videos_async (service,
+ query,
+ NULL,
+ NULL,
+ NULL,
+ (GAsyncReadyCallback) search_cb,
+ os);
+}
+
/* ================== API Implementation ================ */
static const GList *
@@ -537,8 +1035,8 @@ grl_youtube_source_search (GrlMediaSource *source,
os->callback = ss->callback;
os->user_data = ss->user_data;
os->error_code = GRL_ERROR_SEARCH_FAILED;
- os->query = gdata_query_new_with_limits (ss->text, ss->skip, ss->count);
+ os->query = gdata_query_new_with_limits (ss->text, ss->skip, ss->count);
gdata_youtube_service_query_videos_async (GRL_YOUTUBE_SOURCE (source)->service,
os->query,
NULL,
@@ -549,11 +1047,56 @@ grl_youtube_source_search (GrlMediaSource *source,
}
static void
+grl_youtube_source_browse (GrlMediaSource *source,
+ GrlMediaSourceBrowseSpec *bs)
+{
+ OperationSpec *os;
+ const gchar *container_id;
+
+ g_debug ("grl_youtube_source_browse: %s", grl_media_get_id (bs->container));
+
+ container_id = grl_media_get_id (bs->container);
+
+ os = g_new0 (OperationSpec, 1);
+ os->source = bs->source;
+ os->operation_id = bs->browse_id;
+ os->container_id = container_id;
+ os->keys = bs->keys;
+ os->flags = bs->flags;
+ os->skip = bs->skip;
+ os->count = bs->count;
+ os->callback = bs->callback;
+ os->user_data = bs->user_data;
+ os->error_code = GRL_ERROR_BROWSE_FAILED;
+
+ switch (classify_media_id (container_id))
+ {
+ case YOUTUBE_MEDIA_TYPE_ROOT:
+ produce_from_directory (root_dir, root_dir_size, os);
+ break;
+ case YOUTUBE_MEDIA_TYPE_FEEDS:
+ produce_from_directory (feeds_dir, feeds_dir_size, os);
+ break;
+ case YOUTUBE_MEDIA_TYPE_CATEGORIES:
+ produce_from_directory (categories_dir, categories_dir_size, os);
+ break;
+ case YOUTUBE_MEDIA_TYPE_FEED:
+ produce_from_feed (os);
+ break;
+ case YOUTUBE_MEDIA_TYPE_CATEGORY:
+ produce_from_category (os);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+static void
grl_youtube_source_metadata (GrlMediaSource *source,
GrlMediaSourceMetadataSpec *ms)
{
g_debug ("grl_youtube_source_metadata");
-
gdata_youtube_service_query_single_video_async (GRL_YOUTUBE_SOURCE (source)->service,
NULL,
grl_media_get_id (ms->media),
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]