/* -*- 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-content
* @short_description: Media RSS content element
* @stability: Stable
* @include: gdata/media/gdata-media-content.h
*
* #GDataMediaContent represents a "content" element from the
* <ulink type="http" url="http://video.search.yahoo.com/mrss">Media RSS specification</ulink>.
*
* The class only implements parsing, not XML output, at the moment.
*
* Since: 0.4.0
*/
#include <glib.h>
#include <libxml/parser.h>
#include "gdata-media-content.h"
#include "gdata-download-stream.h"
#include "gdata-parsable.h"
#include "gdata-parser.h"
#include "gdata-media-enums.h"
#include "gdata-private.h"
static void gdata_media_content_finalize (GObject *object);
static void gdata_media_content_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
static gboolean pre_parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *root_node, gpointer user_data, GError **error);
static void get_namespaces (GDataParsable *parsable, GHashTable *namespaces);
struct _GDataMediaContentPrivate {
gchar *uri;
gsize filesize;
gchar *content_type;
GDataMediaMedium medium;
gboolean is_default;
GDataMediaExpression expression;
gint64 duration;
guint height;
guint width;
/* TODO: implement other properties from the Media RSS standard */
};
enum {
PROP_URI = 1,
PROP_FILESIZE,
PROP_CONTENT_TYPE,
PROP_MEDIUM,
PROP_IS_DEFAULT,
PROP_EXPRESSION,
PROP_DURATION,
PROP_HEIGHT,
PROP_WIDTH
};
G_DEFINE_TYPE (GDataMediaContent, gdata_media_content, GDATA_TYPE_PARSABLE)
static void
gdata_media_content_class_init (GDataMediaContentClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GDataParsableClass *parsable_class = GDATA_PARSABLE_CLASS (klass);
g_type_class_add_private (klass, sizeof (GDataMediaContentPrivate));
gobject_class->get_property = gdata_media_content_get_property;
gobject_class->finalize = gdata_media_content_finalize;
parsable_class->pre_parse_xml = pre_parse_xml;
parsable_class->get_namespaces = get_namespaces;
parsable_class->element_name = "content";
parsable_class->element_namespace = "media";
/**
* GDataMediaContent:uri:
*
* The direct URI to the media object.
*
* For more information, see the <ulink type="http" url="http://video.search.yahoo.com/mrss">Media RSS specification</ulink>.
*
* Since: 0.4.0
*/
g_object_class_install_property (gobject_class, PROP_URI,
g_param_spec_string ("uri",
"URI", "The direct URI to the media object.",
NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* GDataMediaContent:filesize:
*
* The number of bytes of the media object.
*
* For more information, see the <ulink type="http" url="http://video.search.yahoo.com/mrss">Media RSS specification</ulink>.
*
* Since: 0.4.0
*/
g_object_class_install_property (gobject_class, PROP_FILESIZE,
g_param_spec_ulong ("filesize",
"Filesize", "The number of bytes of the media object.",
0, G_MAXULONG, 0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* GDataMediaContent:content-type:
*
* The standard MIME type of the object.
*
* For more information, see the <ulink type="http" url="http://video.search.yahoo.com/mrss">Media RSS specification</ulink>.
*
* Since: 0.4.0
*/
g_object_class_install_property (gobject_class, PROP_CONTENT_TYPE,
g_param_spec_string ("content-type",
"Content type", "The standard MIME type of the object.",
NULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* GDataMediaContent:medium:
*
* The type of object, complementing #GDataMediaContent:content-type. It allows the consuming application to make simpler decisions between
* different content objects, based on whether they're a video or audio stream, for example.
*
* For more information, see the <ulink type="http" url="http://video.search.yahoo.com/mrss">Media RSS specification</ulink>.
*
* Since: 0.4.0
*/
g_object_class_install_property (gobject_class, PROP_MEDIUM,
g_param_spec_enum ("medium",
"Medium", "The type of object.",
GDATA_TYPE_MEDIA_MEDIUM, GDATA_MEDIA_UNKNOWN,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* GDataMediaContent:is-default:
*
* Determines if this is the default content for the media group. There should only be one default object per media group.
*
* For more information, see the <ulink type="http" url="http://video.search.yahoo.com/mrss">Media RSS specification</ulink>.
*
* Since: 0.4.0
*/
g_object_class_install_property (gobject_class, PROP_IS_DEFAULT,
g_param_spec_boolean ("is-default",
"Default?", "Determines if this is the default content for the media group.",
FALSE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* GDataMediaContent:expression:
*
* Determines if the object is a sample or the full version of the object, or even if it is a continuous stream.
*
* For more information, see the <ulink type="http" url="http://video.search.yahoo.com/mrss">Media RSS specification</ulink>.
*
* Since: 0.4.0
*/
g_object_class_install_property (gobject_class, PROP_EXPRESSION,
g_param_spec_enum ("expression",
"Expression", "Determines if the object is a sample or the full version of the object.",
GDATA_TYPE_MEDIA_EXPRESSION, GDATA_MEDIA_EXPRESSION_FULL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* GDataMediaContent:duration:
*
* The number of seconds for which the media object plays.
*
* For more information, see the <ulink type="http" url="http://video.search.yahoo.com/mrss">Media RSS specification</ulink>.
*
* Since: 0.4.0
*/
g_object_class_install_property (gobject_class, PROP_DURATION,
g_param_spec_int64 ("duration",
"Duration", "The number of seconds for which the media object plays.",
0, G_MAXINT64, 0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* GDataMediaContent:height:
*
* The height of the media object.
*
* For more information, see the <ulink type="http" url="http://video.search.yahoo.com/mrss">Media RSS specification</ulink>.
*
* Since: 0.4.0
*/
g_object_class_install_property (gobject_class, PROP_HEIGHT,
g_param_spec_uint ("height",
"Height", "The height of the media object.",
0, G_MAXUINT, 0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
/**
* GDataMediaContent:width:
*
* The width of the media object.
*
* For more information, see the <ulink type="http" url="http://video.search.yahoo.com/mrss">Media RSS specification</ulink>.
*
* Since: 0.4.0
*/
g_object_class_install_property (gobject_class, PROP_WIDTH,
g_param_spec_uint ("width",
"Width", "The width of the media object.",
0, G_MAXUINT, 0,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
}
static void
gdata_media_content_init (GDataMediaContent *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GDATA_TYPE_MEDIA_CONTENT, GDataMediaContentPrivate);
}
static void
gdata_media_content_finalize (GObject *object)
{
GDataMediaContentPrivate *priv = GDATA_MEDIA_CONTENT (object)->priv;
g_free (priv->uri);
g_free (priv->content_type);
/* Chain up to the parent class */
G_OBJECT_CLASS (gdata_media_content_parent_class)->finalize (object);
}
static void
gdata_media_content_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
{
GDataMediaContentPrivate *priv = GDATA_MEDIA_CONTENT (object)->priv;
switch (property_id) {
case PROP_URI:
g_value_set_string (value, priv->uri);
break;
case PROP_FILESIZE:
g_value_set_ulong (value, priv->filesize);
break;
case PROP_CONTENT_TYPE:
g_value_set_string (value, priv->content_type);
break;
case PROP_MEDIUM:
g_value_set_enum (value, priv->medium);
break;
case PROP_IS_DEFAULT:
g_value_set_boolean (value, priv->is_default);
break;
case PROP_EXPRESSION:
g_value_set_enum (value, priv->expression);
break;
case PROP_DURATION:
g_value_set_int64 (value, priv->duration);
break;
case PROP_HEIGHT:
g_value_set_uint (value, priv->height);
break;
case PROP_WIDTH:
g_value_set_uint (value, priv->width);
break;
default:
/* We don't have any other property... */
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static gboolean
pre_parse_xml (GDataParsable *parsable, xmlDoc *doc, xmlNode *root_node, gpointer user_data, GError **error)
{
GDataMediaContentPrivate *priv = GDATA_MEDIA_CONTENT (parsable)->priv;
xmlChar *uri, *expression, *medium, *duration, *filesize, *height, *width;
gboolean is_default_bool;
GDataMediaExpression expression_enum;
GDataMediaMedium medium_enum;
guint height_uint, width_uint;
gint64 duration_int64;
gulong filesize_ulong;
/* Parse isDefault */
if (gdata_parser_boolean_from_property (root_node, "isDefault", &is_default_bool, 0, error) == FALSE)
return FALSE;
/* Parse expression */
expression = xmlGetProp (root_node, (xmlChar*) "expression");
if (expression == NULL || xmlStrcmp (expression, (xmlChar*) "full") == 0) {
expression_enum = GDATA_MEDIA_EXPRESSION_FULL;
} else if (xmlStrcmp (expression, (xmlChar*) "sample") == 0) {
expression_enum = GDATA_MEDIA_EXPRESSION_SAMPLE;
} else if (xmlStrcmp (expression, (xmlChar*) "nonstop") == 0) {
expression_enum = GDATA_MEDIA_EXPRESSION_NONSTOP;
} else {
gdata_parser_error_unknown_property_value (root_node, "expression", (gchar*) expression, error);
xmlFree (expression);
return FALSE;
}
xmlFree (expression);
/* Parse medium */
medium = xmlGetProp (root_node, (xmlChar*) "medium");
if (medium == NULL) {
medium_enum = GDATA_MEDIA_UNKNOWN;
} else if (xmlStrcmp (medium, (xmlChar*) "image") == 0) {
medium_enum = GDATA_MEDIA_IMAGE;
} else if (xmlStrcmp (medium, (xmlChar*) "audio") == 0) {
medium_enum = GDATA_MEDIA_AUDIO;
} else if (xmlStrcmp (medium, (xmlChar*) "video") == 0) {
medium_enum = GDATA_MEDIA_VIDEO;
} else if (xmlStrcmp (medium, (xmlChar*) "document") == 0) {
medium_enum = GDATA_MEDIA_DOCUMENT;
} else if (xmlStrcmp (medium, (xmlChar*) "executable") == 0) {
medium_enum = GDATA_MEDIA_EXECUTABLE;
} else {
gdata_parser_error_unknown_property_value (root_node, "medium", (gchar*) medium, error);
xmlFree (medium);
return FALSE;
}
xmlFree (medium);
/* Parse duration */
duration = xmlGetProp (root_node, (xmlChar*) "duration");
duration_int64 = (duration == NULL) ? 0 : g_ascii_strtoll ((gchar*) duration, NULL, 10);
xmlFree (duration);
/* Parse filesize */
filesize = xmlGetProp (root_node, (xmlChar*) "fileSize");
filesize_ulong = (filesize == NULL) ? 0 : g_ascii_strtoull ((gchar*) filesize, NULL, 10);
xmlFree (filesize);
/* Parse height and width */
height = xmlGetProp (root_node, (xmlChar*) "height");
height_uint = (height == NULL) ? 0 : g_ascii_strtoull ((gchar*) height, NULL, 10);
xmlFree (height);
width = xmlGetProp (root_node, (xmlChar*) "width");
width_uint = (width == NULL) ? 0 : g_ascii_strtoull ((gchar*) width, NULL, 10);
xmlFree (width);
/* Other properties */
uri = xmlGetProp (root_node, (xmlChar*) "url");
if (uri != NULL && *uri == '\0') {
xmlFree (uri);
return gdata_parser_error_required_property_missing (root_node, "url", error);
}
priv->uri = (gchar*) uri;
priv->filesize = filesize_ulong;
priv->content_type = (gchar*) xmlGetProp (root_node, (xmlChar*) "type");
priv->medium = medium_enum;
priv->is_default = is_default_bool;
priv->expression = expression_enum;
priv->duration = duration_int64;
priv->height = height_uint;
priv->width = width_uint;
return TRUE;
}
static void
get_namespaces (GDataParsable *parsable, GHashTable *namespaces)
{
g_hash_table_insert (namespaces, (gchar*) "media", (gchar*) "http://search.yahoo.com/mrss/");
}
/**
* gdata_media_content_get_uri:
* @self: a #GDataMediaContent
*
* Gets the #GDataMediaContent:uri property.
*
* Return value: the content's URI
*
* Since: 0.4.0
*/
const gchar *
gdata_media_content_get_uri (GDataMediaContent *self)
{
g_return_val_if_fail (GDATA_IS_MEDIA_CONTENT (self), NULL);
return self->priv->uri;
}
/**
* gdata_media_content_get_filesize:
* @self: a #GDataMediaContent
*
* Gets the #GDataMediaContent:filesize property.
*
* Return value: the number of bytes in the content, or <code class="literal">0</code>
*
* Since: 0.4.0
*/
gsize
gdata_media_content_get_filesize (GDataMediaContent *self)
{
g_return_val_if_fail (GDATA_IS_MEDIA_CONTENT (self), 0);
return self->priv->filesize;
}
/**
* gdata_media_content_get_content_type:
* @self: a #GDataMediaContent
*
* Gets the #GDataMediaContent:content-type property.
*
* Return value: the content's content (MIME) type, or %NULL
*
* Since: 0.4.0
*/
const gchar *
gdata_media_content_get_content_type (GDataMediaContent *self)
{
g_return_val_if_fail (GDATA_IS_MEDIA_CONTENT (self), NULL);
return self->priv->content_type;
}
/**
* gdata_media_content_get_medium:
* @self: a #GDataMediaContent
*
* Gets the #GDataMediaContent:medium property.
*
* Return value: the type of the content, or %GDATA_MEDIA_UNKNOWN
*
* Since: 0.4.0
*/
GDataMediaMedium
gdata_media_content_get_medium (GDataMediaContent *self)
{
g_return_val_if_fail (GDATA_IS_MEDIA_CONTENT (self), GDATA_MEDIA_UNKNOWN);
return self->priv->medium;
}
/**
* gdata_media_content_is_default:
* @self: a #GDataMediaContent
*
* Gets the #GDataMediaContent:is-default property.
*
* Return value: %TRUE if the #GDataMediaContent is the default content for the media group, %FALSE otherwise
*
* Since: 0.4.0
*/
gboolean
gdata_media_content_is_default (GDataMediaContent *self)
{
g_return_val_if_fail (GDATA_IS_MEDIA_CONTENT (self), FALSE);
return self->priv->is_default;
}
/**
* gdata_media_content_get_expression:
* @self: a #GDataMediaContent
*
* Gets the #GDataMediaContent:expression property.
*
* Return value: the content's expression, or %GDATA_MEDIA_EXPRESSION_FULL
*
* Since: 0.4.0
*/
GDataMediaExpression
gdata_media_content_get_expression (GDataMediaContent *self)
{
g_return_val_if_fail (GDATA_IS_MEDIA_CONTENT (self), GDATA_MEDIA_EXPRESSION_FULL);
return self->priv->expression;
}
/**
* gdata_media_content_get_duration:
* @self: a #GDataMediaContent
*
* Gets the #GDataMediaContent:duration property.
*
* Return value: the content's duration in seconds, or <code class="literal">0</code>
*
* Since: 0.4.0
*/
gint64
gdata_media_content_get_duration (GDataMediaContent *self)
{
g_return_val_if_fail (GDATA_IS_MEDIA_CONTENT (self), 0);
return self->priv->duration;
}
/**
* gdata_media_content_get_height:
* @self: a #GDataMediaContent
*
* Gets the #GDataMediaContent:height property.
*
* Return value: the content's height in pixels, or <code class="literal">0</code>
*
* Since: 0.4.0
*/
guint
gdata_media_content_get_height (GDataMediaContent *self)
{
g_return_val_if_fail (GDATA_IS_MEDIA_CONTENT (self), 0);
return self->priv->height;
}
/**
* gdata_media_content_get_width:
* @self: a #GDataMediaContent
*
* Gets the #GDataMediaContent:width property.
*
* Return value: the content's width in pixels, or <code class="literal">0</code>
*
* Since: 0.4.0
*/
guint
gdata_media_content_get_width (GDataMediaContent *self)
{
g_return_val_if_fail (GDATA_IS_MEDIA_CONTENT (self), 0);
return self->priv->width;
}
/**
* gdata_media_content_download:
* @self: a #GDataMediaContent
* @service: the #GDataService
* @cancellable: (allow-none): a #GCancellable for the entire download stream, or %NULL
* @error: a #GError, or %NULL
*
* Downloads and returns a #GDataDownloadStream allowing the content represented by @self to be read.
*
* To get the content type of the downloaded data, gdata_download_stream_get_content_type() can be called on the returned #GDataDownloadStream.
* Calling gdata_download_stream_get_content_length() on the stream will not return a meaningful result, however, as the stream is encoded in chunks,
* rather than by content length.
*
* In order to cancel the download, a #GCancellable passed in to @cancellable must be cancelled using g_cancellable_cancel(). Cancelling the individual
* #GInputStream operations on the #GDataDownloadStream will not cancel the entire download; merely the read or close operation in question. See the
* #GDataDownloadStream:cancellable for more details.
*
* Return value: (transfer full): a #GDataDownloadStream to download the content with, or %NULL; unref with g_object_unref()
*
* Since: 0.8.0
*/
GDataDownloadStream *
gdata_media_content_download (GDataMediaContent *self, GDataService *service, GCancellable *cancellable, GError **error)
{
const gchar *src_uri;
g_return_val_if_fail (GDATA_IS_MEDIA_CONTENT (self), NULL);
g_return_val_if_fail (GDATA_IS_SERVICE (service), NULL);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
/* We keep a GError in the argument list so that we can add authentication errors, etc., in future if necessary */
/* Get the download URI and create a stream for it */
src_uri = gdata_media_content_get_uri (self);
return GDATA_DOWNLOAD_STREAM (gdata_download_stream_new (service, NULL, src_uri, cancellable));
}