1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /*
3  * GData Client
4  * Copyright (C) Philip Withnall 2009–2010 <philip@tecnocode.co.uk>
5  *
6  * GData Client is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * GData Client is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with GData Client.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 /*
21  * SECTION:gdata-media-group
22  * @short_description: Media RSS group element
23  * @stability: Stable
24  * @include: gdata/media/gdata-media-group.h
25  *
26  * #GDataMediaGroup represents a "group" element from the
27  * <ulink type="http" url="http://video.search.yahoo.com/mrss">Media RSS specification</ulink>.
28  *
29  * It is private API, since implementing classes are likely to proxy the properties and functions
30  * of #GDataMediaGroup as appropriate; most entry types which implement #GDataMediaGroup have no use
31  * for most of its properties, and it would be unnecessary and confusing to expose #GDataMediaGroup itself.
32  *
33  * For this reason, properties have not been implemented on #GDataMediaGroup (yet).
34  */
35 
36 #include <glib.h>
37 #include <libxml/parser.h>
38 #include <string.h>
39 
40 #include "gdata-media-group.h"
41 #include "gdata-parsable.h"
42 #include "gdata-parser.h"
43 #include "gdata-private.h"
44 #include "media/gdata-media-category.h"
45 #include "media/gdata-media-credit.h"
46 #include "media/gdata-media-thumbnail.h"
47 
48 static void gdata_media_group_dispose (GObject *object);
49 static void gdata_media_group_finalize (GObject *object);
50 static gboolean parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_data, GError **error);
51 static void get_xml (GDataParsable *parsable, GString *xml_string);
52 static void get_namespaces (GDataParsable *parsable, GHashTable *namespaces);
53 
54 struct _GDataMediaGroupPrivate {
55 	gchar **keywords;
56 	gchar *player_uri;
57 	GHashTable *restricted_countries;
58 	gchar *simple_rating;
59 	gchar *mpaa_rating;
60 	gchar *v_chip_rating;
61 	GList *thumbnails; /* GDataMediaThumbnail */
62 	gchar *title;
63 	GDataMediaCategory *category;
64 	GList *contents; /* GDataMediaContent */
65 	GDataMediaCredit *credit;
66 	gchar *description;
67 };
68 
G_DEFINE_TYPE(GDataMediaGroup,gdata_media_group,GDATA_TYPE_PARSABLE)69 G_DEFINE_TYPE (GDataMediaGroup, gdata_media_group, GDATA_TYPE_PARSABLE)
70 
71 static void
72 gdata_media_group_class_init (GDataMediaGroupClass *klass)
73 {
74 	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
75 	GDataParsableClass *parsable_class = GDATA_PARSABLE_CLASS (klass);
76 
77 	g_type_class_add_private (klass, sizeof (GDataMediaGroupPrivate));
78 
79 	gobject_class->dispose = gdata_media_group_dispose;
80 	gobject_class->finalize = gdata_media_group_finalize;
81 
82 	parsable_class->parse_xml = parse_xml;
83 	parsable_class->get_xml = get_xml;
84 	parsable_class->get_namespaces = get_namespaces;
85 	parsable_class->element_name = "group";
86 	parsable_class->element_namespace = "media";
87 }
88 
89 static void
gdata_media_group_init(GDataMediaGroup * self)90 gdata_media_group_init (GDataMediaGroup *self)
91 {
92 	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_MEDIA_GROUP, GDataMediaGroupPrivate);
93 	self->priv->restricted_countries = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
94 }
95 
96 static void
gdata_media_group_dispose(GObject * object)97 gdata_media_group_dispose (GObject *object)
98 {
99 	GDataMediaGroupPrivate *priv = GDATA_MEDIA_GROUP (object)->priv;
100 
101 	if (priv->category != NULL)
102 		g_object_unref (priv->category);
103 	priv->category = NULL;
104 
105 	if (priv->credit != NULL)
106 		g_object_unref (priv->credit);
107 	priv->credit = NULL;
108 
109 	g_list_free_full (priv->contents, g_object_unref);
110 	priv->contents = NULL;
111 
112 	g_list_free_full (priv->thumbnails, g_object_unref);
113 	priv->thumbnails = NULL;
114 
115 	/* Chain up to the parent class */
116 	G_OBJECT_CLASS (gdata_media_group_parent_class)->dispose (object);
117 }
118 
119 static void
gdata_media_group_finalize(GObject * object)120 gdata_media_group_finalize (GObject *object)
121 {
122 	GDataMediaGroupPrivate *priv = GDATA_MEDIA_GROUP (object)->priv;
123 
124 	g_strfreev (priv->keywords);
125 	g_free (priv->player_uri);
126 	g_free (priv->v_chip_rating);
127 	g_free (priv->mpaa_rating);
128 	g_free (priv->simple_rating);
129 	g_hash_table_destroy (priv->restricted_countries);
130 	g_free (priv->title);
131 	g_free (priv->description);
132 
133 	/* Chain up to the parent class */
134 	G_OBJECT_CLASS (gdata_media_group_parent_class)->finalize (object);
135 }
136 
137 static gboolean
parse_xml(GDataParsable * parsable,xmlDoc * doc,xmlNode * node,gpointer user_data,GError ** error)138 parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *node, gpointer user_data, GError **error)
139 {
140 	gboolean success;
141 	GDataMediaGroup *self = GDATA_MEDIA_GROUP (parsable);
142 
143 	if (gdata_parser_is_namespace (node, "http://search.yahoo.com/mrss/") == TRUE) {
144 		if (gdata_parser_string_from_element (node, "title", P_NONE, &(self->priv->title), &success, error) == TRUE ||
145 		    gdata_parser_string_from_element (node, "description", P_NONE, &(self->priv->description), &success, error) == TRUE ||
146 		    gdata_parser_object_from_element_setter (node, "category", P_REQUIRED, GDATA_TYPE_MEDIA_CATEGORY,
147 		                                             gdata_media_group_set_category, self, &success, error) == TRUE ||
148 		    gdata_parser_object_from_element_setter (node, "content", P_REQUIRED, GDATA_TYPE_MEDIA_CONTENT,
149 		                                             _gdata_media_group_add_content, self, &success, error) == TRUE ||
150 		    gdata_parser_object_from_element_setter (node, "thumbnail", P_REQUIRED, GDATA_TYPE_MEDIA_THUMBNAIL,
151 		                                             _gdata_media_group_add_thumbnail, self, &success, error) == TRUE ||
152 		    gdata_parser_object_from_element (node, "credit", P_REQUIRED | P_NO_DUPES, GDATA_TYPE_MEDIA_CREDIT,
153 		                                      &(self->priv->credit), &success, error) == TRUE) {
154 			return success;
155 		} else if (xmlStrcmp (node->name, (xmlChar*) "keywords") == 0) {
156 			/* media:keywords */
157 			guint i;
158 			xmlChar *text = xmlNodeListGetString (node->doc, node->children, TRUE);
159 
160 			g_strfreev (self->priv->keywords);
161 			if (text == NULL) {
162 				self->priv->keywords = NULL;
163 				return TRUE;
164 			}
165 
166 			self->priv->keywords = g_strsplit ((gchar*) text, ",", -1);
167 			xmlFree (text);
168 
169 			for (i = 0; self->priv->keywords[i] != NULL; i++) {
170 				gchar *comma, *start = self->priv->keywords[i];
171 				gchar *end = start + strlen (start);
172 
173 				/* Strip any whitespace from the ends of the keyword */
174 				g_strstrip (start);
175 
176 				/* Unescape any %2Cs in the keyword to commas in-place */
177 				while ((comma = g_strstr_len (start, -1, "%2C")) != NULL) {
178 					/* Unescape the comma */
179 					*comma = ',';
180 
181 					/* Move forwards, skipping the comma */
182 					comma++;
183 					end -= 2;
184 
185 					/* Shift the remainder of the string downwards */
186 					memmove (comma, comma + 2, end - comma);
187 					*end = '\0';
188 				}
189 			}
190 		} else if (xmlStrcmp (node->name, (xmlChar*) "player") == 0) {
191 			/* media:player */
192 			xmlChar *player_uri = xmlGetProp (node, (xmlChar*) "url");
193 			g_free (self->priv->player_uri);
194 			self->priv->player_uri = (gchar*) player_uri;
195 		} else if (xmlStrcmp (node->name, (xmlChar*) "rating") == 0) {
196 			/* media:rating */
197 			xmlChar *scheme;
198 
199 			/* The possible schemes are defined here:
200 			 *  • http://video.search.yahoo.com/mrss
201 			 *  • http://code.google.com/apis/youtube/2.0/reference.html#youtube_data_api_tag_media:rating
202 			 */
203 			scheme = xmlGetProp (node, (xmlChar*) "scheme");
204 
205 			if (scheme == NULL || xmlStrcmp (scheme, (xmlChar*) "urn:simple") == 0) {
206 				/* Options: adult, nonadult */
207 				gdata_parser_string_from_element (node, "rating", P_REQUIRED | P_NON_EMPTY, &(self->priv->simple_rating),
208 				                                  &success, error);
209 			} else if (xmlStrcmp (scheme, (xmlChar*) "urn:mpaa") == 0) {
210 				/* Options: g, pg, pg-13, r, nc-17 */
211 				gdata_parser_string_from_element (node, "rating", P_REQUIRED | P_NON_EMPTY, &(self->priv->mpaa_rating),
212 				                                  &success, error);
213 			} else if (xmlStrcmp (scheme, (xmlChar*) "urn:v-chip") == 0) {
214 				/* Options: tv-y, tv-y7, tv-y7-fv, tv-g, tv-pg, tv-14, tv-ma */
215 				gdata_parser_string_from_element (node, "rating", P_REQUIRED | P_NON_EMPTY, &(self->priv->v_chip_rating),
216 				                                  &success, error);
217 			} else if (xmlStrcmp (scheme, (xmlChar*) "http://gdata.youtube.com/schemas/2007#mediarating") == 0) {
218 				/* No content, but we do get a list of countries. There's nothing like overloading the semantics of XML elements
219 				 * to brighten up one's day. */
220 				xmlChar *countries;
221 
222 				countries = xmlGetProp (node, (xmlChar*) "country");
223 
224 				if (countries != NULL) {
225 					gchar **country_list, **country;
226 
227 					/* It's either a comma-separated list of countries, or the value "all" */
228 					country_list = g_strsplit ((const gchar*) countries, ",", -1);
229 					xmlFree (countries);
230 
231 					/* Add all the listed countries to the restricted countries table */
232 					for (country = country_list; *country != NULL; country++) {
233 						g_hash_table_insert (self->priv->restricted_countries, *country, GUINT_TO_POINTER (TRUE));
234 					}
235 
236 					g_free (country_list);
237 				} else {
238 					/* Assume it's restricted in all countries */
239 					g_hash_table_insert (self->priv->restricted_countries, g_strdup ("all"), GUINT_TO_POINTER (TRUE));
240 				}
241 
242 				success = TRUE;
243 			} else {
244 				/* Error */
245 				gdata_parser_error_unknown_property_value (node, "scheme", (gchar*) scheme, error);
246 				success = FALSE;
247 			}
248 
249 			xmlFree (scheme);
250 
251 			return success;
252 		} else if (xmlStrcmp (node->name, (xmlChar*) "restriction") == 0) {
253 			/* media:restriction */
254 			xmlChar *type, *countries, *relationship;
255 			gchar **country_list, **country;
256 			gboolean relationship_bool;
257 
258 			/* Check the type property is "country" */
259 			type = xmlGetProp (node, (xmlChar*) "type");
260 			if (xmlStrcmp (type, (xmlChar*) "country") != 0) {
261 				gdata_parser_error_unknown_property_value (node, "type", (gchar*) type, error);
262 				xmlFree (type);
263 				return FALSE;
264 			}
265 			xmlFree (type);
266 
267 			relationship = xmlGetProp (node, (xmlChar*) "relationship");
268 			if (xmlStrcmp (relationship, (xmlChar*) "allow") == 0) {
269 				relationship_bool = FALSE; /* it's *not* a restricted country */
270 			} else if (xmlStrcmp (relationship, (xmlChar*) "deny") == 0) {
271 				relationship_bool = TRUE; /* it *is* a restricted country */
272 			} else {
273 				gdata_parser_error_unknown_property_value (node, "relationship", (gchar*) relationship, error);
274 				xmlFree (relationship);
275 				return FALSE;
276 			}
277 			xmlFree (relationship);
278 
279 			countries = xmlNodeListGetString (doc, node->children, TRUE);
280 			country_list = g_strsplit ((const gchar*) countries, " ", -1);
281 			xmlFree (countries);
282 
283 			/* Add "all" to the table, since it's an exception table */
284 			g_hash_table_insert (self->priv->restricted_countries, g_strdup ("all"), GUINT_TO_POINTER (!relationship_bool));
285 
286 			/* Add all the listed countries to the restricted countries table */
287 			for (country = country_list; *country != NULL; country++)
288 				g_hash_table_insert (self->priv->restricted_countries, *country, GUINT_TO_POINTER (relationship_bool));
289 			g_free (country_list);
290 		} else {
291 			return GDATA_PARSABLE_CLASS (gdata_media_group_parent_class)->parse_xml (parsable, doc, node, user_data, error);
292 		}
293 	} else {
294 		return GDATA_PARSABLE_CLASS (gdata_media_group_parent_class)->parse_xml (parsable, doc, node, user_data, error);
295 	}
296 
297 	return TRUE;
298 }
299 
300 static void
get_xml(GDataParsable * parsable,GString * xml_string)301 get_xml (GDataParsable *parsable, GString *xml_string)
302 {
303 	GDataMediaGroupPrivate *priv = GDATA_MEDIA_GROUP (parsable)->priv;
304 
305 	/* Media category */
306 	if (priv->category != NULL)
307 		_gdata_parsable_get_xml (GDATA_PARSABLE (priv->category), xml_string, FALSE);
308 
309 	if (priv->title != NULL)
310 		gdata_parser_string_append_escaped (xml_string, "<media:title type='plain'>", priv->title, "</media:title>");
311 
312 	if (priv->description != NULL)
313 		gdata_parser_string_append_escaped (xml_string, "<media:description type='plain'>", priv->description, "</media:description>");
314 
315 	if (priv->keywords != NULL) {
316 		guint i;
317 
318 		g_string_append (xml_string, "<media:keywords>");
319 
320 		/* Add each keyword to the text content, comma-separated from the previous one */
321 		for (i = 0; priv->keywords[i] != NULL; i++) {
322 			const gchar *comma, *start = priv->keywords[i];
323 
324 			/* Delimit the previous keyword */
325 			if (i != 0)
326 				g_string_append_c (xml_string, ',');
327 
328 			/* Escape any commas in the keyword to %2C */
329 			while ((comma = g_utf8_strchr (start, -1, ',')) != NULL) {
330 				/* Copy the span */
331 				gchar *span = g_strndup (start, comma - start);
332 				gdata_parser_string_append_escaped (xml_string, NULL, span, NULL);
333 				g_free (span);
334 
335 				/* Add an escaped comma */
336 				g_string_append (xml_string, "%2C");
337 
338 				/* Move forwards, skipping the comma */
339 				start = comma + 1;
340 			}
341 
342 			/* Append the rest of the string (the entire string if there were no commas) */
343 			gdata_parser_string_append_escaped (xml_string, NULL, start, NULL);
344 		}
345 
346 		g_string_append (xml_string, "</media:keywords>");
347 	}
348 }
349 
350 static void
get_namespaces(GDataParsable * parsable,GHashTable * namespaces)351 get_namespaces (GDataParsable *parsable, GHashTable *namespaces)
352 {
353 	g_hash_table_insert (namespaces, (gchar*) "media", (gchar*) "http://search.yahoo.com/mrss/");
354 }
355 
356 /**
357  * gdata_media_group_get_title:
358  * @self: a #GDataMediaGroup
359  *
360  * Gets the #GDataMediaGroup:title property.
361  *
362  * Return value: the group's title, or %NULL
363  *
364  * Since: 0.4.0
365  */
366 const gchar *
gdata_media_group_get_title(GDataMediaGroup * self)367 gdata_media_group_get_title (GDataMediaGroup *self)
368 {
369 	g_return_val_if_fail (GDATA_IS_MEDIA_GROUP (self), NULL);
370 	return self->priv->title;
371 }
372 
373 /**
374  * gdata_media_group_set_title:
375  * @self: a #GDataMediaGroup
376  * @title: (allow-none): the group's new title, or %NULL
377  *
378  * Sets the #GDataMediaGroup:title property to @title.
379  *
380  * Set @title to %NULL to unset the property.
381  *
382  * Since: 0.4.0
383  */
384 void
gdata_media_group_set_title(GDataMediaGroup * self,const gchar * title)385 gdata_media_group_set_title (GDataMediaGroup *self, const gchar *title)
386 {
387 	g_return_if_fail (GDATA_IS_MEDIA_GROUP (self));
388 	g_free (self->priv->title);
389 	self->priv->title = g_strdup (title);
390 }
391 
392 /**
393  * gdata_media_group_get_description:
394  * @self: a #GDataMediaGroup
395  *
396  * Gets the #GDataMediaGroup:description property.
397  *
398  * Return value: the group's description, or %NULL
399  *
400  * Since: 0.4.0
401  */
402 const gchar *
gdata_media_group_get_description(GDataMediaGroup * self)403 gdata_media_group_get_description (GDataMediaGroup *self)
404 {
405 	g_return_val_if_fail (GDATA_IS_MEDIA_GROUP (self), NULL);
406 	return self->priv->description;
407 }
408 
409 /**
410  * gdata_media_group_set_description:
411  * @self: a #GDataMediaGroup
412  * @description: (allow-none): the group's new description, or %NULL
413  *
414  * Sets the #GDataMediaGroup:description property to @description.
415  *
416  * Set @description to %NULL to unset the property.
417  *
418  * Since: 0.4.0
419  */
420 void
gdata_media_group_set_description(GDataMediaGroup * self,const gchar * description)421 gdata_media_group_set_description (GDataMediaGroup *self, const gchar *description)
422 {
423 	g_return_if_fail (GDATA_IS_MEDIA_GROUP (self));
424 	g_free (self->priv->description);
425 	self->priv->description = g_strdup (description);
426 }
427 
428 /**
429  * gdata_media_group_get_keywords:
430  * @self: a #GDataMediaGroup
431  *
432  * Gets the #GDataMediaGroup:keywords property.
433  *
434  * Return value: (array zero-terminated=1): a %NULL-terminated array of the group's keywords, or %NULL
435  *
436  * Since: 0.4.0
437  */
438 const gchar * const *
gdata_media_group_get_keywords(GDataMediaGroup * self)439 gdata_media_group_get_keywords (GDataMediaGroup *self)
440 {
441 	g_return_val_if_fail (GDATA_IS_MEDIA_GROUP (self), NULL);
442 	return (const gchar * const *) self->priv->keywords;
443 }
444 
445 /**
446  * gdata_media_group_set_keywords:
447  * @self: a #GDataMediaGroup
448  * @keywords: (array zero-terminated=1) (allow-none): a %NULL-terminated array of the group's new keywords, or %NULL
449  *
450  * Sets the #GDataMediaGroup:keywords property to @keywords.
451  *
452  * Set @keywords to %NULL to unset the property.
453  *
454  * Since: 0.4.0
455  */
456 void
gdata_media_group_set_keywords(GDataMediaGroup * self,const gchar * const * keywords)457 gdata_media_group_set_keywords (GDataMediaGroup *self, const gchar * const *keywords)
458 {
459 	g_return_if_fail (GDATA_IS_MEDIA_GROUP (self));
460 	g_strfreev (self->priv->keywords);
461 	self->priv->keywords = g_strdupv ((gchar**) keywords);
462 }
463 
464 /**
465  * gdata_media_group_get_category:
466  * @self: a #GDataMediaGroup
467  *
468  * Gets the #GDataMediaGroup:category property.
469  *
470  * Return value: a #GDataMediaCategory giving the group's category, or %NULL
471  */
472 GDataMediaCategory *
gdata_media_group_get_category(GDataMediaGroup * self)473 gdata_media_group_get_category (GDataMediaGroup *self)
474 {
475 	g_return_val_if_fail (GDATA_IS_MEDIA_GROUP (self), NULL);
476 	return self->priv->category;
477 }
478 
479 /**
480  * gdata_media_group_set_category:
481  * @self: a #GDataMediaGroup
482  * @category: (allow-none): a new #GDataMediaCategory, or %NULL
483  *
484  * Sets the #GDataMediaGroup:category property to @category, and increments its reference count.
485  */
486 void
gdata_media_group_set_category(GDataMediaGroup * self,GDataMediaCategory * category)487 gdata_media_group_set_category (GDataMediaGroup *self, GDataMediaCategory *category)
488 {
489 	g_return_if_fail (GDATA_IS_MEDIA_GROUP (self));
490 	g_return_if_fail (category == NULL || GDATA_IS_MEDIA_CATEGORY (category));
491 
492 	if (self->priv->category != NULL)
493 		g_object_unref (self->priv->category);
494 	self->priv->category = (category == NULL) ? NULL : g_object_ref (category);
495 }
496 
497 static gint
content_compare_cb(const GDataMediaContent * content,const gchar * type)498 content_compare_cb (const GDataMediaContent *content, const gchar *type)
499 {
500 	return strcmp (gdata_media_content_get_content_type ((GDataMediaContent*) content), type);
501 }
502 
503 /**
504  * gdata_media_group_look_up_content:
505  * @self: a #GDataMediaGroup
506  * @type: the MIME type of the content desired
507  *
508  * Looks up a #GDataMediaContent from the group with the given MIME type. The group's list of contents is
509  * a list of URIs to various formats of the group content itself, such as the SWF URI or RTSP stream for a video.
510  *
511  * Return value: (transfer none): a #GDataMediaContent matching @type, or %NULL
512  */
513 GDataMediaContent *
gdata_media_group_look_up_content(GDataMediaGroup * self,const gchar * type)514 gdata_media_group_look_up_content (GDataMediaGroup *self, const gchar *type)
515 {
516 	GList *element;
517 
518 	g_return_val_if_fail (GDATA_IS_MEDIA_GROUP (self), NULL);
519 	g_return_val_if_fail (type != NULL, NULL);
520 
521 	/* TODO: If type is required, and is unique, the contents can be stored in a hash table rather than a linked list */
522 	element = g_list_find_custom (self->priv->contents, type, (GCompareFunc) content_compare_cb);
523 	if (element == NULL)
524 		return NULL;
525 	return GDATA_MEDIA_CONTENT (element->data);
526 }
527 
528 /**
529  * gdata_media_group_get_contents:
530  * @self: a #GDataMediaGroup
531  *
532  * Returns a list of #GDataMediaContents, giving the content enclosed by the group.
533  *
534  * Return value: (element-type GData.MediaContent) (transfer none): a #GList of #GDataMediaContents,  or %NULL
535  */
536 GList *
gdata_media_group_get_contents(GDataMediaGroup * self)537 gdata_media_group_get_contents (GDataMediaGroup *self)
538 {
539 	g_return_val_if_fail (GDATA_IS_MEDIA_GROUP (self), NULL);
540 	return self->priv->contents;
541 }
542 
543 void
_gdata_media_group_add_content(GDataMediaGroup * self,GDataMediaContent * content)544 _gdata_media_group_add_content (GDataMediaGroup *self, GDataMediaContent *content)
545 {
546 	g_return_if_fail (GDATA_IS_MEDIA_GROUP (self));
547 	g_return_if_fail (GDATA_IS_MEDIA_CONTENT (content));
548 	self->priv->contents = g_list_prepend (self->priv->contents, g_object_ref (content));
549 }
550 
551 /**
552  * gdata_media_group_get_credit:
553  * @self: a #GDataMediaGroup
554  *
555  * Gets the #GDataMediaGroup:credit property.
556  *
557  * Return value: a #GDataMediaCredit giving information on whom to credit for the media group, or %NULL
558  */
559 GDataMediaCredit *
gdata_media_group_get_credit(GDataMediaGroup * self)560 gdata_media_group_get_credit (GDataMediaGroup *self)
561 {
562 	g_return_val_if_fail (GDATA_IS_MEDIA_GROUP (self), NULL);
563 	return self->priv->credit;
564 }
565 
566 void
_gdata_media_group_set_credit(GDataMediaGroup * self,GDataMediaCredit * credit)567 _gdata_media_group_set_credit (GDataMediaGroup *self, GDataMediaCredit *credit)
568 {
569 	g_return_if_fail (GDATA_IS_MEDIA_GROUP (self));
570 	g_return_if_fail (credit == NULL ||GDATA_IS_MEDIA_CREDIT (credit));
571 
572 	if (self->priv->credit != NULL)
573 		g_object_unref (self->priv->credit);
574 	self->priv->credit = g_object_ref (credit);
575 }
576 
577 /**
578  * gdata_media_group_get_media_group:
579  * @self: a #GDataMediaGroup
580  *
581  * Gets the #GDataMediaGroup:player-uri property.
582  *
583  * Return value: a URI where the media group is playable in a web browser, or %NULL
584  */
585 const gchar *
gdata_media_group_get_player_uri(GDataMediaGroup * self)586 gdata_media_group_get_player_uri (GDataMediaGroup *self)
587 {
588 	g_return_val_if_fail (GDATA_IS_MEDIA_GROUP (self), NULL);
589 	return self->priv->player_uri;
590 }
591 
592 /**
593  * gdata_media_group_is_restricted_in_country:
594  * @self: a #GDataMediaGroup
595  * @country: an ISO 3166 two-letter country code to check
596  *
597  * Checks whether viewing of the media is restricted in @country, either by its content rating, or by the request of the producer.
598  * The return value from this function is purely informational, and no obligation is assumed.
599  *
600  * Return value: %TRUE if the media is restricted in @country, %FALSE otherwise
601  */
602 gboolean
gdata_media_group_is_restricted_in_country(GDataMediaGroup * self,const gchar * country)603 gdata_media_group_is_restricted_in_country (GDataMediaGroup *self, const gchar *country)
604 {
605 	g_return_val_if_fail (GDATA_IS_MEDIA_GROUP (self), FALSE);
606 	g_return_val_if_fail (country != NULL && *country != '\0', FALSE);
607 
608 	if (GPOINTER_TO_UINT (g_hash_table_lookup (self->priv->restricted_countries, country)) == TRUE)
609 		return TRUE;
610 
611 	return GPOINTER_TO_UINT (g_hash_table_lookup (self->priv->restricted_countries, "all"));
612 }
613 
614 /**
615  * gdata_media_group_get_media_rating:
616  * @self: a #GDataMediaGroup
617  * @rating_type: the type of rating to retrieve
618  *
619  * 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.
620  * The valid values for @rating_type are: <code class="literal">simple</code>, <code class="literal">mpaa</code> and
621  * <code class="literal">v-chip</code>.
622  *
623  * The rating values returned for each of these rating types are string as defined in the
624  * <ulink type="http" url="http://code.google.com/apis/youtube/2.0/reference.html#youtube_data_api_tag_media:rating">YouTube documentation</ulink> and
625  * <ulink type="http" url="http://video.search.yahoo.com/mrss">MRSS specification</ulink>.
626  *
627  * Return value: rating for the given rating type, or %NULL if the media has no rating for that type (or the type is invalid)
628  */
629 const gchar *
gdata_media_group_get_media_rating(GDataMediaGroup * self,const gchar * rating_type)630 gdata_media_group_get_media_rating (GDataMediaGroup *self, const gchar *rating_type)
631 {
632 	g_return_val_if_fail (GDATA_IS_MEDIA_GROUP (self), NULL);
633 	g_return_val_if_fail (rating_type != NULL && *rating_type != '\0', NULL);
634 
635 	if (strcmp (rating_type, "simple") == 0) {
636 		return self->priv->simple_rating;
637 	} else if (strcmp (rating_type, "mpaa") == 0) {
638 		return self->priv->mpaa_rating;
639 	} else if (strcmp (rating_type, "v-chip") == 0) {
640 		return self->priv->v_chip_rating;
641 	}
642 
643 	return NULL;
644 }
645 
646 /**
647  * gdata_media_group_get_thumbnails:
648  * @self: a #GDataMediaGroup
649  *
650  * Gets a list of the thumbnails available for the group.
651  *
652  * Return value: (element-type GData.MediaThumbnail) (transfer none): a #GList of #GDataMediaThumbnails, or %NULL
653  */
654 GList *
gdata_media_group_get_thumbnails(GDataMediaGroup * self)655 gdata_media_group_get_thumbnails (GDataMediaGroup *self)
656 {
657 	g_return_val_if_fail (GDATA_IS_MEDIA_GROUP (self), NULL);
658 	return self->priv->thumbnails;
659 }
660 
661 void
_gdata_media_group_add_thumbnail(GDataMediaGroup * self,GDataMediaThumbnail * thumbnail)662 _gdata_media_group_add_thumbnail (GDataMediaGroup *self, GDataMediaThumbnail *thumbnail)
663 {
664 	g_return_if_fail (GDATA_IS_MEDIA_GROUP (self));
665 	g_return_if_fail (GDATA_IS_MEDIA_THUMBNAIL (thumbnail));
666 	self->priv->thumbnails = g_list_prepend (self->priv->thumbnails, g_object_ref (thumbnail));
667 }
668