[Rhythmbox-devel] Atom feed podcast support



Hi,

The attached patch hacks in basic support for Atom podcast feeds - but
I'm pretty sure it's not the best way of doing things. It solved my
immediate problem, and that's about it.

I've read that the plan is to move the podcasting into a Python plugin -
is anyone working on this yet?

-- 
Tim Retout <tim retout co uk>
Index: podcast/rb-podcast-parse.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/podcast/rb-podcast-parse.c,v
retrieving revision 1.30
diff -u -r1.30 rb-podcast-parse.c
--- podcast/rb-podcast-parse.c	14 Jul 2006 12:28:24 -0000	1.30
+++ podcast/rb-podcast-parse.c	13 Sep 2006 22:43:02 -0000
@@ -54,19 +54,30 @@
 		RB_PODCAST_PARSER_STATE_IMG_PROPERTY,
 		RB_PODCAST_PARSER_STATE_ITEM,
 		RB_PODCAST_PARSER_STATE_ITEM_PROPERTY,
+		RB_PODCAST_PARSER_STATE_ATOM_FEED,
+		RB_PODCAST_PARSER_STATE_ATOM_FEED_PROPERTY,
+		RB_PODCAST_PARSER_STATE_ATOM_ENTRY,
+		RB_PODCAST_PARSER_STATE_ATOM_ENTRY_PROPERTY,
+		RB_PODCAST_PARSER_STATE_ATOM_ENTRY_AUTHOR,
+		RB_PODCAST_PARSER_STATE_ATOM_ENTRY_AUTHOR_PROPERTY,
 		RB_PODCAST_PARSER_STATE_END,
 	} state;
 };
 
 static gboolean rb_validate_channel_propert (const char *name);
 static gboolean rb_validate_item_propert (const char *name);
+static gboolean rb_validate_atom_feed_propert (const char *name);
+static gboolean rb_validate_atom_entry_propert (const char *name);
 static uintmax_t rb_podcast_parse_date (const char* date_str);
+static uintmax_t rb_podcast_parse_atom_date (const char* date_str);
 static gulong rb_podcast_parse_time (const char *time_str);
 static void rb_podcast_parser_start_element (struct RBPoadcastLoadContext* ctx, const char *name, const char **attrs);
 static void rb_podcast_parser_end_element (struct RBPoadcastLoadContext* ctx, const char *name);
 static void rb_podcast_parser_characters (struct RBPoadcastLoadContext* ctx, const char *data, guint len);
 static void rb_set_channel_value (struct RBPoadcastLoadContext* ctx, const char* name, const char* value);
 static void rb_set_item_value (struct RBPoadcastLoadContext* ctx, const char* name, const char* value);
+static void rb_set_atom_feed_value (struct RBPoadcastLoadContext* ctx, const char* name, const char* value);
+static void rb_set_atom_entry_value (struct RBPoadcastLoadContext* ctx, const char* name, const char* value);
 
 static RBPodcastItem *
 rb_podcast_initializa_item ()
@@ -153,6 +164,82 @@
 }
 
 static void
+rb_set_atom_feed_value (struct RBPoadcastLoadContext *ctx,
+			const char *name,
+			const char *value)
+{
+	xmlChar *dvalue;
+
+	if (value == NULL)
+		return;
+
+	if (name == NULL)
+		return;
+
+	dvalue = xmlCharStrdup (value);
+	g_strstrip ((char *)dvalue);
+   
+	if (!strcmp (name, "title")) {
+		ctx->channel_data->title = dvalue;
+	} else if (!strcmp (name, "subtitle")) {
+		ctx->channel_data->subtitle = dvalue;
+	} else if (!strcmp (name, "tagline")) {
+		ctx->channel_data->description = dvalue;
+	} else if (!strcmp (name, "generator")) {
+		if (ctx->channel_data->author == NULL)
+			ctx->channel_data->author = dvalue;
+	} else if (!strcmp (name, "icon")) {
+		ctx->channel_data->img = dvalue;
+	} else if (!strcmp (name, "updated")) {
+		ctx->channel_data->pub_date = rb_podcast_parse_atom_date ((char *)dvalue);
+		g_free (dvalue);
+	} else if (!strcmp (name, "modified")) {
+		if (!ctx->channel_data->pub_date) {
+			ctx->channel_data->pub_date = rb_podcast_parse_atom_date ((char *)dvalue);
+			g_free (dvalue);
+		}
+	} else if (!strcmp (name, "rights")) {
+		ctx->channel_data->copyright = dvalue;
+	} else if (!strcmp (name, "copyright")) {
+		if (ctx->channel_data->copyright == NULL)
+			ctx->channel_data->copyright = dvalue;
+	} else {
+		g_free (dvalue);
+	}
+}
+
+static void
+rb_set_atom_entry_value (struct RBPoadcastLoadContext *ctx,
+			 const char *name,
+			 const char *value)
+{
+	xmlChar *dvalue;
+
+	dvalue = xmlCharStrdup (value);
+	g_strstrip ((char *)dvalue);
+
+	if (!strcmp (name, "title")) {
+		ctx->item_data->title = dvalue;
+	} else if (!strcmp (name, "summary")) {
+		ctx->item_data->description = dvalue;
+	} else if (!strcmp (name, "updated")) {
+		ctx->item_data->pub_date = rb_podcast_parse_atom_date ((char *)dvalue);
+		g_free (dvalue);
+	} else if (!strcmp (name, "modified")) {
+		if (!ctx->item_data->pub_date) {
+			ctx->item_data->pub_date = rb_podcast_parse_atom_date ((char *)dvalue);
+			g_free (dvalue);
+		}
+	} else if (!strcmp (name, "href")) {
+		ctx->item_data->url = dvalue;
+	} else if (!strcmp (name, "length")) {
+		ctx->item_data->filesize = g_ascii_strtoull ((char *)dvalue, NULL, 10);
+	} else {
+		g_free (dvalue);
+	}
+}
+
+static void
 rb_insert_item (struct RBPoadcastLoadContext *ctx)
 {
 	RBPodcastItem *data = ctx->item_data;
@@ -212,6 +299,67 @@
 	}
 }
 
+static gboolean
+rb_validate_atom_feed_propert (const char *name)
+{
+	if (name == NULL) {
+		return FALSE;
+	}
+
+	if (!strcmp (name, "category") ||
+	    !strcmp (name, "contributor") ||
+	    !strcmp (name, "generator") ||
+	    !strcmp (name, "icon") ||
+	    !strcmp (name, "id") ||
+	    !strcmp (name, "link") ||
+	    !strcmp (name, "logo") ||
+	    !strcmp (name, "rights") ||
+	    !strcmp (name, "subtitle") ||
+	    !strcmp (name, "title") ||
+	    !strcmp (name, "updated") ||
+	    /* Atom 0.3 elements */
+	    !strcmp (name, "copyright") ||
+	    !strcmp (name, "info") ||
+	    !strcmp (name, "modified") ||
+	    !strcmp (name, "tagline")) {
+
+		return TRUE;
+	} else {
+		return FALSE;
+	}
+
+}
+
+static gboolean
+rb_validate_atom_entry_propert (const char *name)
+{
+	if (name == NULL) {
+		return FALSE;
+	}
+
+	if (!strcmp (name, "author") ||
+	    !strcmp (name, "category") ||
+	    !strcmp (name, "content") ||
+	    !strcmp (name, "contributor") ||
+	    !strcmp (name, "id") ||
+	    !strcmp (name, "link") ||
+	    !strcmp (name, "published") ||
+	    !strcmp (name, "rights") ||
+	    !strcmp (name, "source") ||
+	    !strcmp (name, "summary") ||
+	    !strcmp (name, "title") ||
+	    !strcmp (name, "updated") ||
+	    /* Atom 0.3 elements */
+	    !strcmp (name, "created") ||
+	    !strcmp (name, "issued") ||
+	    !strcmp (name, "modified")) {
+	    
+		return TRUE;
+	} else {
+		return FALSE;
+	}
+}
+
 static void
 rb_podcast_parser_start_element (struct RBPoadcastLoadContext *ctx,
 				 const char *name,
@@ -225,6 +373,8 @@
 		{
 			if (!strcmp (name, "rss")) {
 				ctx->state = RB_PODCAST_PARSER_STATE_RSS;
+			} else if (!strcmp (name, "feed")) {
+				ctx->state = RB_PODCAST_PARSER_STATE_ATOM_FEED;
 			} else {
 				ctx->in_unknown_elt++;
 			}
@@ -297,9 +447,73 @@
 			break;
 		}
 
+	case RB_PODCAST_PARSER_STATE_ATOM_FEED:
+		{
+			if (!strcmp (name, "entry")) {
+				ctx->item_data = rb_podcast_initializa_item ();
+				ctx->state = RB_PODCAST_PARSER_STATE_ATOM_ENTRY;
+			} else if (!rb_validate_atom_feed_propert (name)) {
+				rb_debug ("Unknown property");
+				ctx->in_unknown_elt++;
+			} else {
+				ctx->state = RB_PODCAST_PARSER_STATE_ATOM_FEED_PROPERTY;
+			}
+
+			break;
+		}
+
+	case RB_PODCAST_PARSER_STATE_ATOM_ENTRY:
+		{
+			if (!strcmp (name, "author")) {
+				ctx->state = RB_PODCAST_PARSER_STATE_ATOM_ENTRY_AUTHOR;
+			} else if (!strcmp (name, "link")) {
+				const char *rel_value, *href_value, *length_value;
+				rel_value = href_value = length_value = NULL;
+
+				for (; *attrs; attrs +=2) {
+					if (!strcmp (*attrs, "rel")) {
+						rel_value = *(attrs + 1);
+					} else if (!strcmp (*attrs, "href")) {
+						href_value = *(attrs + 1);
+					} else if (!strcmp (*attrs, "length")) {
+						length_value = *(attrs + 1);
+					}
+				}
+
+				if (rel_value && href_value && length_value &&
+				    !strcmp (rel_value, "enclosure")) {
+					rb_set_atom_entry_value (ctx, "href", href_value);
+					rb_set_atom_entry_value (ctx, "length", length_value);
+				}
+
+				ctx->state = RB_PODCAST_PARSER_STATE_ATOM_ENTRY_PROPERTY;
+
+			} else if (!rb_validate_atom_entry_propert (name)) {
+				ctx->in_unknown_elt++;
+			} else {
+				ctx->state = RB_PODCAST_PARSER_STATE_ATOM_ENTRY_PROPERTY;
+			}
+
+			break;
+		}
+
+	case RB_PODCAST_PARSER_STATE_ATOM_ENTRY_AUTHOR:
+		{
+			if (!strcmp (name, "name")) {
+				ctx->state = RB_PODCAST_PARSER_STATE_ATOM_ENTRY_AUTHOR_PROPERTY;
+			} else {
+				ctx->in_unknown_elt++;
+			}
+
+			break;
+		}
+
         case RB_PODCAST_PARSER_STATE_CHANNEL_PROPERTY:
         case RB_PODCAST_PARSER_STATE_ITEM_PROPERTY:
         case RB_PODCAST_PARSER_STATE_IMG_PROPERTY:
+	case RB_PODCAST_PARSER_STATE_ATOM_FEED_PROPERTY:
+	case RB_PODCAST_PARSER_STATE_ATOM_ENTRY_PROPERTY:
+	case RB_PODCAST_PARSER_STATE_ATOM_ENTRY_AUTHOR_PROPERTY:
 		rb_debug ("nested element inside property; treating as unknown");
 		ctx->in_unknown_elt++;
 		break;
@@ -372,6 +586,45 @@
 		ctx->state = RB_PODCAST_PARSER_STATE_CHANNEL;
 		break;
 
+	case RB_PODCAST_PARSER_STATE_ATOM_FEED:
+		ctx->state = RB_PODCAST_PARSER_STATE_START;
+		break;
+
+	case RB_PODCAST_PARSER_STATE_ATOM_FEED_PROPERTY:
+		{
+			rb_set_atom_feed_value (ctx, name, ctx->prop_value->str);
+			ctx->state = RB_PODCAST_PARSER_STATE_ATOM_FEED;
+			g_string_truncate (ctx->prop_value, 0);
+			break;
+		}
+
+	case RB_PODCAST_PARSER_STATE_ATOM_ENTRY:
+		{
+			rb_insert_item (ctx);
+			ctx->state = RB_PODCAST_PARSER_STATE_ATOM_FEED;
+			break;
+		}
+
+	case RB_PODCAST_PARSER_STATE_ATOM_ENTRY_PROPERTY:
+		{
+			rb_set_atom_entry_value (ctx, name, ctx->prop_value->str);
+			ctx->state = RB_PODCAST_PARSER_STATE_ATOM_ENTRY;
+			g_string_truncate (ctx->prop_value, 0);
+			break;
+		}
+
+	case RB_PODCAST_PARSER_STATE_ATOM_ENTRY_AUTHOR:
+		ctx->state = RB_PODCAST_PARSER_STATE_ATOM_ENTRY;
+		break;
+
+	case RB_PODCAST_PARSER_STATE_ATOM_ENTRY_AUTHOR_PROPERTY:
+		{
+			rb_set_atom_entry_value (ctx, "author", ctx->prop_value->str);
+			ctx->state = RB_PODCAST_PARSER_STATE_ATOM_ENTRY_AUTHOR;
+			g_string_truncate (ctx->prop_value, 0);
+			break;
+		}
+
         case RB_PODCAST_PARSER_STATE_END:
 		break;
 
@@ -390,6 +643,9 @@
         case RB_PODCAST_PARSER_STATE_CHANNEL_PROPERTY:
         case RB_PODCAST_PARSER_STATE_ITEM_PROPERTY:
         case RB_PODCAST_PARSER_STATE_IMG_PROPERTY:
+	case RB_PODCAST_PARSER_STATE_ATOM_FEED_PROPERTY:
+	case RB_PODCAST_PARSER_STATE_ATOM_ENTRY_PROPERTY:
+	case RB_PODCAST_PARSER_STATE_ATOM_ENTRY_AUTHOR_PROPERTY:
 		g_string_append_len (ctx->prop_value, data, len);
            	break;
         case RB_PODCAST_PARSER_STATE_START:
@@ -397,6 +653,9 @@
         case RB_PODCAST_PARSER_STATE_RSS:
         case RB_PODCAST_PARSER_STATE_CHANNEL:
         case RB_PODCAST_PARSER_STATE_ITEM:
+	case RB_PODCAST_PARSER_STATE_ATOM_FEED:
+	case RB_PODCAST_PARSER_STATE_ATOM_ENTRY:
+	case RB_PODCAST_PARSER_STATE_ATOM_ENTRY_AUTHOR:
         case RB_PODCAST_PARSER_STATE_END:
 		break;
 	default:
@@ -420,7 +679,9 @@
 
 	data->url = xmlCharStrdup (file_name);
 
-	if (!g_str_has_suffix (file_name, ".rss") && !g_str_has_suffix (file_name, ".xml")) {
+	if (!g_str_has_suffix (file_name, ".rss") &&
+	    !g_str_has_suffix (file_name, ".xml") &&
+	    !g_str_has_suffix (file_name, ".atom") ) {
 		gboolean invalid_mime_type;
 
 		info = gnome_vfs_file_info_new ();
@@ -602,6 +863,22 @@
 
 	if (result == NULL) {
 		memset (&tm, 0, sizeof (struct tm));
+		rb_debug ("unable to convert date string %s", date_str);
+	}
+
+	return (uintmax_t) mktime (&tm);
+}
+
+static uintmax_t
+rb_podcast_parse_atom_date (const char *date_str)
+{
+	struct tm tm;
+	char *result;
+
+	memset (&tm, 0, sizeof (struct tm));	
+	result = strptime (date_str, "%Y-%m-%dT%T%z", &tm);
+	if (result == NULL) {
+		memset (&tm, 0, sizeof (struct tm));	
 		rb_debug ("unable to convert date string %s", date_str);
 	}
 


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