Blob Blame History Raw
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/*
 * GData Client
 * Copyright (C) Philip Withnall 2009–2010 <philip@tecnocode.co.uk>
 *
 * GData Client is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * GData Client is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with GData Client.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * SECTION:gdata-media-group
 * @short_description: Media RSS group element
 * @stability: Stable
 * @include: gdata/media/gdata-media-group.h
 *
 * #GDataMediaGroup represents a "group" element from the
 * <ulink type="http" url="http://video.search.yahoo.com/mrss">Media RSS specification</ulink>.
 *
 * It is private API, since implementing classes are likely to proxy the properties and functions
 * of #GDataMediaGroup as appropriate; most entry types which implement #GDataMediaGroup have no use
 * for most of its properties, and it would be unnecessary and confusing to expose #GDataMediaGroup itself.
 *
 * For this reason, properties have not been implemented on #GDataMediaGroup (yet).
 */

#include <glib.h>
#include <libxml/parser.h>
#include <string.h>

#include "gdata-media-group.h"
#include "gdata-parsable.h"
#include "gdata-parser.h"
#include "gdata-private.h"
#include "media/gdata-media-category.h"
#include "media/gdata-media-credit.h"
#include "media/gdata-media-thumbnail.h"

static void gdata_media_group_dispose (GObject *object);
static void gdata_media_group_finalize (GObject *object);
static gboolean parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_data, GError **error);
static void get_xml (GDataParsable *parsable, GString *xml_string);
static void get_namespaces (GDataParsable *parsable, GHashTable *namespaces);

struct _GDataMediaGroupPrivate {
	gchar **keywords;
	gchar *player_uri;
	GHashTable *restricted_countries;
	gchar *simple_rating;
	gchar *mpaa_rating;
	gchar *v_chip_rating;
	GList *thumbnails; /* GDataMediaThumbnail */
	gchar *title;
	GDataMediaCategory *category;
	GList *contents; /* GDataMediaContent */
	GDataMediaCredit *credit;
	gchar *description;
};

G_DEFINE_TYPE (GDataMediaGroup, gdata_media_group, GDATA_TYPE_PARSABLE)

static void
gdata_media_group_class_init (GDataMediaGroupClass *klass)
{
	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
	GDataParsableClass *parsable_class = GDATA_PARSABLE_CLASS (klass);

	g_type_class_add_private (klass, sizeof (GDataMediaGroupPrivate));

	gobject_class->dispose = gdata_media_group_dispose;
	gobject_class->finalize = gdata_media_group_finalize;

	parsable_class->parse_xml = parse_xml;
	parsable_class->get_xml = get_xml;
	parsable_class->get_namespaces = get_namespaces;
	parsable_class->element_name = "group";
	parsable_class->element_namespace = "media";
}

static void
gdata_media_group_init (GDataMediaGroup *self)
{
	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_MEDIA_GROUP, GDataMediaGroupPrivate);
	self->priv->restricted_countries = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
}

static void
gdata_media_group_dispose (GObject *object)
{
	GDataMediaGroupPrivate *priv = GDATA_MEDIA_GROUP (object)->priv;

	if (priv->category != NULL)
		g_object_unref (priv->category);
	priv->category = NULL;

	if (priv->credit != NULL)
		g_object_unref (priv->credit);
	priv->credit = NULL;

	if (priv->contents != NULL) {
		g_list_foreach (priv->contents, (GFunc) g_object_unref, NULL);
		g_list_free (priv->contents);
	}
	priv->contents = NULL;

	if (priv->thumbnails != NULL) {
		g_list_foreach (priv->thumbnails, (GFunc) g_object_unref, NULL);
		g_list_free (priv->thumbnails);
	}
	priv->thumbnails = NULL;

	/* Chain up to the parent class */
	G_OBJECT_CLASS (gdata_media_group_parent_class)->dispose (object);
}

static void
gdata_media_group_finalize (GObject *object)
{
	GDataMediaGroupPrivate *priv = GDATA_MEDIA_GROUP (object)->priv;

	g_strfreev (priv->keywords);
	g_free (priv->player_uri);
	g_free (priv->v_chip_rating);
	g_free (priv->mpaa_rating);
	g_free (priv->simple_rating);
	g_hash_table_destroy (priv->restricted_countries);
	g_free (priv->title);
	g_free (priv->description);

	/* Chain up to the parent class */
	G_OBJECT_CLASS (gdata_media_group_parent_class)->finalize (object);
}

static gboolean
parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_data, GError **error)
{
	gboolean success;
	GDataMediaGroup *self = GDATA_MEDIA_GROUP (parsable);

	if (gdata_parser_is_namespace (node, "http://search.yahoo.com/mrss/") == TRUE) {
		if (gdata_parser_string_from_element (node, "title", P_NONE, &(self->priv->title), &success, error) == TRUE ||
		    gdata_parser_string_from_element (node, "description", P_NONE, &(self->priv->description), &success, error) == TRUE ||
		    gdata_parser_object_from_element_setter (node, "category", P_REQUIRED, GDATA_TYPE_MEDIA_CATEGORY,
		                                             gdata_media_group_set_category, self, &success, error) == TRUE ||
		    gdata_parser_object_from_element_setter (node, "content", P_REQUIRED, GDATA_TYPE_MEDIA_CONTENT,
		                                             _gdata_media_group_add_content, self, &success, error) == TRUE ||
		    gdata_parser_object_from_element_setter (node, "thumbnail", P_REQUIRED, GDATA_TYPE_MEDIA_THUMBNAIL,
		                                             _gdata_media_group_add_thumbnail, self, &success, error) == TRUE ||
		    gdata_parser_object_from_element (node, "credit", P_REQUIRED | P_NO_DUPES, GDATA_TYPE_MEDIA_CREDIT,
		                                      &(self->priv->credit), &success, error) == TRUE) {
			return success;
		} else if (xmlStrcmp (node->name, (xmlChar*) "keywords") == 0) {
			/* media:keywords */
			guint i;
			xmlChar *text = xmlNodeListGetString (node->doc, node->children, TRUE);

			g_strfreev (self->priv->keywords);
			if (text == NULL) {
				self->priv->keywords = NULL;
				return TRUE;
			}

			self->priv->keywords = g_strsplit ((gchar*) text, ",", -1);
			xmlFree (text);

			for (i = 0; self->priv->keywords[i] != NULL; i++) {
				gchar *comma, *start = self->priv->keywords[i];
				gchar *end = start + strlen (start);

				/* Strip any whitespace from the ends of the keyword */
				g_strstrip (start);

				/* Unescape any %2Cs in the keyword to commas in-place */
				while ((comma = g_strstr_len (start, -1, "%2C")) != NULL) {
					/* Unescape the comma */
					*comma = ',';

					/* Move forwards, skipping the comma */
					comma++;
					end -= 2;

					/* Shift the remainder of the string downwards */
					g_memmove (comma, comma + 2, end - comma);
					*end = '\0';
				}
			}
		} else if (xmlStrcmp (node->name, (xmlChar*) "player") == 0) {
			/* media:player */
			xmlChar *player_uri = xmlGetProp (node, (xmlChar*) "url");
			g_free (self->priv->player_uri);
			self->priv->player_uri = (gchar*) player_uri;
		} else if (xmlStrcmp (node->name, (xmlChar*) "rating") == 0) {
			/* media:rating */
			xmlChar *scheme;

			/* The possible schemes are defined here:
			 *  • http://video.search.yahoo.com/mrss
			 *  • http://code.google.com/apis/youtube/2.0/reference.html#youtube_data_api_tag_media:rating
			 */
			scheme = xmlGetProp (node, (xmlChar*) "scheme");

			if (scheme == NULL || xmlStrcmp (scheme, (xmlChar*) "urn:simple") == 0) {
				/* Options: adult, nonadult */
				gdata_parser_string_from_element (node, "rating", P_REQUIRED | P_NON_EMPTY, &(self->priv->simple_rating),
				                                  &success, error);
			} else if (xmlStrcmp (scheme, (xmlChar*) "urn:mpaa") == 0) {
				/* Options: g, pg, pg-13, r, nc-17 */
				gdata_parser_string_from_element (node, "rating", P_REQUIRED | P_NON_EMPTY, &(self->priv->mpaa_rating),
				                                  &success, error);
			} else if (xmlStrcmp (scheme, (xmlChar*) "urn:v-chip") == 0) {
				/* Options: tv-y, tv-y7, tv-y7-fv, tv-g, tv-pg, tv-14, tv-ma */
				gdata_parser_string_from_element (node, "rating", P_REQUIRED | P_NON_EMPTY, &(self->priv->v_chip_rating),
				                                  &success, error);
			} else if (xmlStrcmp (scheme, (xmlChar*) "http://gdata.youtube.com/schemas/2007#mediarating") == 0) {
				/* No content, but we do get a list of countries. There's nothing like overloading the semantics of XML elements
				 * to brighten up one's day. */
				xmlChar *countries;

				countries = xmlGetProp (node, (xmlChar*) "country");

				if (countries != NULL) {
					gchar **country_list, **country;

					/* It's either a comma-separated list of countries, or the value "all" */
					country_list = g_strsplit ((const gchar*) countries, ",", -1);
					xmlFree (countries);

					/* Add all the listed countries to the restricted countries table */
					for (country = country_list; *country != NULL; country++) {
						g_hash_table_insert (self->priv->restricted_countries, *country, GUINT_TO_POINTER (TRUE));
					}

					g_free (country_list);
				} else {
					/* Assume it's restricted in all countries */
					g_hash_table_insert (self->priv->restricted_countries, g_strdup ("all"), GUINT_TO_POINTER (TRUE));
				}

				success = TRUE;
			} else {
				/* Error */
				gdata_parser_error_unknown_property_value (node, "scheme", (gchar*) scheme, error);
				success = FALSE;
			}

			xmlFree (scheme);

			return success;
		} else if (xmlStrcmp (node->name, (xmlChar*) "restriction") == 0) {
			/* media:restriction */
			xmlChar *type, *countries, *relationship;
			gchar **country_list, **country;
			gboolean relationship_bool;

			/* Check the type property is "country" */
			type = xmlGetProp (node, (xmlChar*) "type");
			if (xmlStrcmp (type, (xmlChar*) "country") != 0) {
				gdata_parser_error_unknown_property_value (node, "type", (gchar*) type, error);
				xmlFree (type);
				return FALSE;
			}
			xmlFree (type);

			relationship = xmlGetProp (node, (xmlChar*) "relationship");
			if (xmlStrcmp (relationship, (xmlChar*) "allow") == 0) {
				relationship_bool = FALSE; /* it's *not* a restricted country */
			} else if (xmlStrcmp (relationship, (xmlChar*) "deny") == 0) {
				relationship_bool = TRUE; /* it *is* a restricted country */
			} else {
				gdata_parser_error_unknown_property_value (node, "relationship", (gchar*) relationship, error);
				xmlFree (relationship);
				return FALSE;
			}
			xmlFree (relationship);

			countries = xmlNodeListGetString (doc, node->children, TRUE);
			country_list = g_strsplit ((const gchar*) countries, " ", -1);
			xmlFree (countries);

			/* Add "all" to the table, since it's an exception table */
			g_hash_table_insert (self->priv->restricted_countries, g_strdup ("all"), GUINT_TO_POINTER (!relationship_bool));

			/* Add all the listed countries to the restricted countries table */
			for (country = country_list; *country != NULL; country++)
				g_hash_table_insert (self->priv->restricted_countries, *country, GUINT_TO_POINTER (relationship_bool));
			g_free (country_list);
		} else {
			return GDATA_PARSABLE_CLASS (gdata_media_group_parent_class)->parse_xml (parsable, doc, node, user_data, error);
		}
	} else {
		return GDATA_PARSABLE_CLASS (gdata_media_group_parent_class)->parse_xml (parsable, doc, node, user_data, error);
	}

	return TRUE;
}

static void
get_xml (GDataParsable *parsable, GString *xml_string)
{
	GDataMediaGroupPrivate *priv = GDATA_MEDIA_GROUP (parsable)->priv;

	/* Media category */
	if (priv->category != NULL)
		_gdata_parsable_get_xml (GDATA_PARSABLE (priv->category), xml_string, FALSE);

	if (priv->title != NULL)
		gdata_parser_string_append_escaped (xml_string, "<media:title type='plain'>", priv->title, "</media:title>");

	if (priv->description != NULL)
		gdata_parser_string_append_escaped (xml_string, "<media:description type='plain'>", priv->description, "</media:description>");

	if (priv->keywords != NULL) {
		guint i;

		g_string_append (xml_string, "<media:keywords>");

		/* Add each keyword to the text content, comma-separated from the previous one */
		for (i = 0; priv->keywords[i] != NULL; i++) {
			const gchar *comma, *start = priv->keywords[i];

			/* Delimit the previous keyword */
			if (i != 0)
				g_string_append_c (xml_string, ',');

			/* Escape any commas in the keyword to %2C */
			while ((comma = g_utf8_strchr (start, -1, ',')) != NULL) {
				/* Copy the span */
				gchar *span = g_strndup (start, comma - start);
				gdata_parser_string_append_escaped (xml_string, NULL, span, NULL);
				g_free (span);

				/* Add an escaped comma */
				g_string_append (xml_string, "%2C");

				/* Move forwards, skipping the comma */
				start = comma + 1;
			}

			/* Append the rest of the string (the entire string if there were no commas) */
			gdata_parser_string_append_escaped (xml_string, NULL, start, NULL);
		}

		g_string_append (xml_string, "</media:keywords>");
	}
}

static void
get_namespaces (GDataParsable *parsable, GHashTable *namespaces)
{
	g_hash_table_insert (namespaces, (gchar*) "media", (gchar*) "http://search.yahoo.com/mrss/");
}

/**
 * gdata_media_group_get_title:
 * @self: a #GDataMediaGroup
 *
 * Gets the #GDataMediaGroup:title property.
 *
 * Return value: the group's title, or %NULL
 *
 * Since: 0.4.0
 */
const gchar *
gdata_media_group_get_title (GDataMediaGroup *self)
{
	g_return_val_if_fail (GDATA_IS_MEDIA_GROUP (self), NULL);
	return self->priv->title;
}

/**
 * gdata_media_group_set_title:
 * @self: a #GDataMediaGroup
 * @title: (allow-none): the group's new title, or %NULL
 *
 * Sets the #GDataMediaGroup:title property to @title.
 *
 * Set @title to %NULL to unset the property.
 *
 * Since: 0.4.0
 */
void
gdata_media_group_set_title (GDataMediaGroup *self, const gchar *title)
{
	g_return_if_fail (GDATA_IS_MEDIA_GROUP (self));
	g_free (self->priv->title);
	self->priv->title = g_strdup (title);
}

/**
 * gdata_media_group_get_description:
 * @self: a #GDataMediaGroup
 *
 * Gets the #GDataMediaGroup:description property.
 *
 * Return value: the group's description, or %NULL
 *
 * Since: 0.4.0
 */
const gchar *
gdata_media_group_get_description (GDataMediaGroup *self)
{
	g_return_val_if_fail (GDATA_IS_MEDIA_GROUP (self), NULL);
	return self->priv->description;
}

/**
 * gdata_media_group_set_description:
 * @self: a #GDataMediaGroup
 * @description: (allow-none): the group's new description, or %NULL
 *
 * Sets the #GDataMediaGroup:description property to @description.
 *
 * Set @description to %NULL to unset the property.
 *
 * Since: 0.4.0
 */
void
gdata_media_group_set_description (GDataMediaGroup *self, const gchar *description)
{
	g_return_if_fail (GDATA_IS_MEDIA_GROUP (self));
	g_free (self->priv->description);
	self->priv->description = g_strdup (description);
}

/**
 * gdata_media_group_get_keywords:
 * @self: a #GDataMediaGroup
 *
 * Gets the #GDataMediaGroup:keywords property.
 *
 * Return value: (array zero-terminated=1): a %NULL-terminated array of the group's keywords, or %NULL
 *
 * Since: 0.4.0
 */
const gchar * const *
gdata_media_group_get_keywords (GDataMediaGroup *self)
{
	g_return_val_if_fail (GDATA_IS_MEDIA_GROUP (self), NULL);
	return (const gchar * const *) self->priv->keywords;
}

/**
 * gdata_media_group_set_keywords:
 * @self: a #GDataMediaGroup
 * @keywords: (array zero-terminated=1) (allow-none): a %NULL-terminated array of the group's new keywords, or %NULL
 *
 * Sets the #GDataMediaGroup:keywords property to @keywords.
 *
 * Set @keywords to %NULL to unset the property.
 *
 * Since: 0.4.0
 */
void
gdata_media_group_set_keywords (GDataMediaGroup *self, const gchar * const *keywords)
{
	g_return_if_fail (GDATA_IS_MEDIA_GROUP (self));
	g_strfreev (self->priv->keywords);
	self->priv->keywords = g_strdupv ((gchar**) keywords);
}

/**
 * gdata_media_group_get_category:
 * @self: a #GDataMediaGroup
 *
 * Gets the #GDataMediaGroup:category property.
 *
 * Return value: a #GDataMediaCategory giving the group's category, or %NULL
 */
GDataMediaCategory *
gdata_media_group_get_category (GDataMediaGroup *self)
{
	g_return_val_if_fail (GDATA_IS_MEDIA_GROUP (self), NULL);
	return self->priv->category;
}

/**
 * gdata_media_group_set_category:
 * @self: a #GDataMediaGroup
 * @category: (allow-none): a new #GDataMediaCategory, or %NULL
 *
 * Sets the #GDataMediaGroup:category property to @category, and increments its reference count.
 */
void
gdata_media_group_set_category (GDataMediaGroup *self, GDataMediaCategory *category)
{
	g_return_if_fail (GDATA_IS_MEDIA_GROUP (self));
	g_return_if_fail (category == NULL || GDATA_IS_MEDIA_CATEGORY (category));

	if (self->priv->category != NULL)
		g_object_unref (self->priv->category);
	self->priv->category = (category == NULL) ? NULL : g_object_ref (category);
}

static gint
content_compare_cb (const GDataMediaContent *content, const gchar *type)
{
	return strcmp (gdata_media_content_get_content_type ((GDataMediaContent*) content), type);
}

/**
 * gdata_media_group_look_up_content:
 * @self: a #GDataMediaGroup
 * @type: the MIME type of the content desired
 *
 * Looks up a #GDataMediaContent from the group with the given MIME type. The group's list of contents is
 * a list of URIs to various formats of the group content itself, such as the SWF URI or RTSP stream for a video.
 *
 * Return value: (transfer none): a #GDataMediaContent matching @type, or %NULL
 */
GDataMediaContent *
gdata_media_group_look_up_content (GDataMediaGroup *self, const gchar *type)
{
	GList *element;

	g_return_val_if_fail (GDATA_IS_MEDIA_GROUP (self), NULL);
	g_return_val_if_fail (type != NULL, NULL);

	/* TODO: If type is required, and is unique, the contents can be stored in a hash table rather than a linked list */
	element = g_list_find_custom (self->priv->contents, type, (GCompareFunc) content_compare_cb);
	if (element == NULL)
		return NULL;
	return GDATA_MEDIA_CONTENT (element->data);
}

/**
 * gdata_media_group_get_contents:
 * @self: a #GDataMediaGroup
 *
 * Returns a list of #GDataMediaContent<!-- -->s, giving the content enclosed by the group.
 *
 * Return value: (element-type GData.MediaContent) (transfer none): a #GList of #GDataMediaContent<!-- -->s,  or %NULL
 */
GList *
gdata_media_group_get_contents (GDataMediaGroup *self)
{
	g_return_val_if_fail (GDATA_IS_MEDIA_GROUP (self), NULL);
	return self->priv->contents;
}

void
_gdata_media_group_add_content (GDataMediaGroup *self, GDataMediaContent *content)
{
	g_return_if_fail (GDATA_IS_MEDIA_GROUP (self));
	g_return_if_fail (GDATA_IS_MEDIA_CONTENT (content));
	self->priv->contents = g_list_prepend (self->priv->contents, g_object_ref (content));
}

/**
 * gdata_media_group_get_credit:
 * @self: a #GDataMediaGroup
 *
 * Gets the #GDataMediaGroup:credit property.
 *
 * Return value: a #GDataMediaCredit giving information on whom to credit for the media group, or %NULL
 */
GDataMediaCredit *
gdata_media_group_get_credit (GDataMediaGroup *self)
{
	g_return_val_if_fail (GDATA_IS_MEDIA_GROUP (self), NULL);
	return self->priv->credit;
}

void
_gdata_media_group_set_credit (GDataMediaGroup *self, GDataMediaCredit *credit)
{
	g_return_if_fail (GDATA_IS_MEDIA_GROUP (self));
	g_return_if_fail (credit == NULL ||GDATA_IS_MEDIA_CREDIT (credit));

	if (self->priv->credit != NULL)
		g_object_unref (self->priv->credit);
	self->priv->credit = g_object_ref (credit);
}

/**
 * gdata_media_group_get_media_group:
 * @self: a #GDataMediaGroup
 *
 * Gets the #GDataMediaGroup:player-uri property.
 *
 * Return value: a URI where the media group is playable in a web browser, or %NULL
 */
const gchar *
gdata_media_group_get_player_uri (GDataMediaGroup *self)
{
	g_return_val_if_fail (GDATA_IS_MEDIA_GROUP (self), NULL);
	return self->priv->player_uri;
}

/**
 * gdata_media_group_is_restricted_in_country:
 * @self: a #GDataMediaGroup
 * @country: an ISO 3166 two-letter country code to check
 *
 * Checks whether viewing of the media is restricted in @country, either by its content rating, or by the request of the producer.
 * The return value from this function is purely informational, and no obligation is assumed.
 *
 * Return value: %TRUE if the media is restricted in @country, %FALSE otherwise
 */
gboolean
gdata_media_group_is_restricted_in_country (GDataMediaGroup *self, const gchar *country)
{
	g_return_val_if_fail (GDATA_IS_MEDIA_GROUP (self), FALSE);
	g_return_val_if_fail (country != NULL && *country != '\0', FALSE);

	if (GPOINTER_TO_UINT (g_hash_table_lookup (self->priv->restricted_countries, country)) == TRUE)
		return TRUE;

	return GPOINTER_TO_UINT (g_hash_table_lookup (self->priv->restricted_countries, "all"));
}

/**
 * gdata_media_group_get_media_rating:
 * @self: a #GDataMediaGroup
 * @rating_type: the type of rating to retrieve
 *
 * Returns the rating of the given type for the media, if one exists. For example, this could be a film rating awarded by the MPAA.
 * The valid values for @rating_type are: <code class="literal">simple</code>, <code class="literal">mpaa</code> and
 * <code class="literal">v-chip</code>.
 *
 * The rating values returned for each of these rating types are string as defined in the
 * <ulink type="http" url="http://code.google.com/apis/youtube/2.0/reference.html#youtube_data_api_tag_media:rating">YouTube documentation</ulink> and
 * <ulink type="http" url="http://video.search.yahoo.com/mrss">MRSS specification</ulink>.
 *
 * Return value: rating for the given rating type, or %NULL if the media has no rating for that type (or the type is invalid)
 */
const gchar *
gdata_media_group_get_media_rating (GDataMediaGroup *self, const gchar *rating_type)
{
	g_return_val_if_fail (GDATA_IS_MEDIA_GROUP (self), NULL);
	g_return_val_if_fail (rating_type != NULL && *rating_type != '\0', NULL);

	if (strcmp (rating_type, "simple") == 0) {
		return self->priv->simple_rating;
	} else if (strcmp (rating_type, "mpaa") == 0) {
		return self->priv->mpaa_rating;
	} else if (strcmp (rating_type, "v-chip") == 0) {
		return self->priv->v_chip_rating;
	}

	return NULL;
}

/**
 * gdata_media_group_get_thumbnails:
 * @self: a #GDataMediaGroup
 *
 * Gets a list of the thumbnails available for the group.
 *
 * Return value: (element-type GData.MediaThumbnail) (transfer none): a #GList of #GDataMediaThumbnail<!-- -->s, or %NULL
 */
GList *
gdata_media_group_get_thumbnails (GDataMediaGroup *self)
{
	g_return_val_if_fail (GDATA_IS_MEDIA_GROUP (self), NULL);
	return self->priv->thumbnails;
}

void
_gdata_media_group_add_thumbnail (GDataMediaGroup *self, GDataMediaThumbnail *thumbnail)
{
	g_return_if_fail (GDATA_IS_MEDIA_GROUP (self));
	g_return_if_fail (GDATA_IS_MEDIA_THUMBNAIL (thumbnail));
	self->priv->thumbnails = g_list_prepend (self->priv->thumbnails, g_object_ref (thumbnail));
}