feedback
Dec 31 2007

C# Template for Creating Google Video Sitemap

by John Dyer

Earlier this month, Google introduced "Video Sitemaps" as an extension to the Sitemap Protocol standard. It allows website owners to expose their video content (including embedded video) to be indexed and included in Google's video search (here is Google's spec). Embrace and extend ;)

I just added this to Dallas Theological Seminary's site: http://www.dts.edu/videositemap.xml and I thought it might help to share the template to speed someone else's development:

using System;
using System.Data;
using System.Configuration;
using System.Collections.Generic;
using System.Web;
using System.Xml;
using System.Text;

namespace YourNamespace
{
    public class VideoSiteMap : IHttpHandler
    {
        public VideoSiteMap()
        {
        } 
        
        public void ProcessRequest(HttpContext context)
        {
            XmlTextWriter writer = new XmlTextWriter(context.Response.OutputStream, Encoding.UTF8); 
            context.Response.ContentType = "text/xml";

            writer.Formatting = Formatting.Indented;
            writer.WriteStartDocument(); 
            writer.WriteStartElement("urlset");
            
            // add namespaces
            writer.WriteAttributeString("xmlns", "http://www.sitemaps.org/schemas/sitemap/0.9");
            writer.WriteAttributeString("xmlns", "video", null, "http://www.google.com/schemas/sitemap-video/1.0");
            
            // fake loop
            for (int i=0; i<10; i++) {
                writer.WriteStartElement("url");

				// required
                writer.WriteElementString("loc", "http://mysite.com/myplayer.aspx");
                writer.WriteStartElement("video", "video", null); 
                
                // start:optional
                writer.WriteElementString("video", "title", null, "Video Title");
                writer.WriteElementString("video", "description", null, "a great video");
                writer.WriteElementString("video", "thumbnail_loc", null, "http://mysite.com/myvideothumb.jpg");
                writer.WriteElementString("video", "family_friendly", null, "Yes");
                writer.WriteElementString("video", "content_loc", null, "http://mysite.com/myvideo.flv");
                writer.WriteElementString("video", "duration", null, "100");                    
                
                writer.WriteStartElement("video", "player_loc", null);                
                writer.WriteAttributeString("allow_embed", "true");
                writer.WriteString("http://mysite.com/embeddedplayer.swf");
                writer.WriteEndElement(); // video:player_loc
                // end:optional
                
                writer.WriteEndElement(); // video:video
                writer.WriteEndElement(); //url
            }
            
            writer.WriteEndElement(); //urlset 
            writer.WriteEndDocument();
            writer.Close();
        }
        public bool IsReusable
        {
            get { return false; }
        }
    }
}

Just add this class to your web.config and you're all set:

<system.web>
   <httpHandlers>
      <add verb="*" path="videositemap.xml" type="YourNamespace.VideoSiteMap, YourNamespace"/>
   </httpHandlers>
</system.web>

Hope that helps!

Dec 15 2007

Stupid JavaScript Tricks: "3D" Panorama

by John Dyer

Update: Please note, this a Firefox-only, totally experimental "fun" project, not meant for real world use :)

I'm putting together a tutorial for making 3D panoramas using Papervision3D (example) and I though I would release some pretty pointless JavaScript code I wrote a while back that attempts to make a panorama using just JavaScript. Here's how it works: it creates a series of div columns, and then puts copies of the image in each column. As you move your mouse across the image, it stretches the columns vertically along a sin curve to fake the 3D look. And then it destroys your processor.

Here's part of a photo before the effect is applied:

image

Here's the photo split into 1px wide columns. Notice how the roof is straightened out. 

image

Unfortunately, its too slow to be usable. So here's the photo split into 5px wide columns, which makes it pretty usable and doesn't look too bad.

 image

Here are links to try it out:

Dec 12 2007

Papervision Coverflow updated for PV3D 2.0 (GreatWhite)

by John Dyer

I've updated my original Papervision Coverflow knockoff from Phunky to GreatWhite. The main change is the use of Renderer and ViewPort, but I also added some code to stop rendering when nothing is moving to ease up on the processor. Make sure to download Papervision and Tweener before compiling it.

Links:

Dec 6 2007

Working with Different Kinds of Data: Using OUTER JOINS and Inheritance

by John Dyer

I am working on combining a lot of data into one engine where items can be searched and linked together, but there are several data type, each with unique data fields. In my case, I am working with (1) audio/video content and (2) text content such as articles and news items. To represent this data in a database, I could make one large table with lots of fields, some common to both data types and some specific to each one. Then this data can be represented with a single class and my ORM is very simple.

Instead I've chosen to use a combination of several tables to separate out the data, but then I join them together on the programming side with inheritance. Here's the data model (simplified for this example), where the shared elements are in the GenericItems table:

CREATE TABLE GenericItems (
    ItemID uniqueidentifier,
    Title nvarchar(255),
    CreationDate datetime,
    IsActive bit,
    Description ntext,
    ItemType int) 

CREATE TABLE TextItems (
    ItemID uniqueidentifier,
    ArticleText ntext,
    AuthorID int,
    MagazineID int)

CREATE TABLE MediaItems (
    ItemID uniqueidentifier,
    Filename nvarchar(50),
    Filesize int,
    Duration int)

I then create three classes to model represent this (again, shortened for space):

public class GenericItem {
    protected Guid _itemID;
    protected string _title;

    public Guid ItemID {
        get { return _itemID; }
        set { _itemID = value; }
    }
    public string Title {
        get { return _title; }
        set { _title = value; }
    }
    // ... other fields
}
public class MediaItem : GenericItem {
    protected string _filename;

    public string Filename {
        get { return _filename; }
        set { _filename = value; }
    }
    // ... other fields
}
public class TextItem : GenericItem {
    protected string _articleText;

    public string ArticleText {
        get { return _articleText; }
        set { _articleText = value; }
    }
    // ... other fields
}

For the data layer, I can now make calls which pull both MediaItem objects and TextItem objects together into a List<T> object with type GenericItem. For example, the following method gets recent items of both types and uses an LEFT OUTER JOIN to bring them into a single query:

public static List<GenericItem> GetRecentItems() {

    List<GenericItem> itemsList = new List<GenericItem>();

    string sql = @"
SELECT 
   TOP 10 * 
FROM 
   GenericItems
   LEFT OUTER JOIN
      TextItems  ON TextItems.ItemID = GenericItems.ItemID
   LEFT OUTER JOIN
      MediaItems ON MediaItems.ItemID = GenericItems.ItemID
ORDER BY 
   CreationDate;";

    // ... connection code
    SqlDataReader sqlDataReader = sqlCommand.ExecuteReader();

    while (sqlDataReader.Read()) {
        object item = null;
        // first, find out which object type this will be, 
        int itemType = (int)dataReader["ItemType"];

        // second, instantiate the object and get its unique fields
        if (itemType == 1) {
           item = new TextItem();
           TextItem textItem = item as TextItem;
           textItem.ArticleText = (string)dataReader["ArticleText"];
           // ... other fields
        } else if (itemType == 2) {
           item = new MediaItem();
           MediaItem mediaItem = item as MediaItem;
           mediaItem.Filename = (string)dataReader["Filename"];
           // ... other fields
        }
        
        // get the properties shared in the GenericItems table
        ContentItem contentItem = item as ContentItem;
        contentItem.ItemID = new Guid(dataReader["ItemID"].ToString());
        contentItem.Title= new (string) dataReader["Title"];
        // ... other fields

        itemsList.Add( (GenericItem) item);
    }

    // ... close connection, etc.

    return itemsList;
}

We now have a List<T> with data in that can be used in other controls. In ASP.NET, this data can be bound to Asp:Repeater controls and which can show data based on the type. In the following example, the repeater shows the Title (which all items have), but then shows either "read" or "watch" depending on the type of the object:

<asp:Repeater id="RecentItemsRepeater" runat="Server" >
<ItemTemplate>
<div class="item">
<%# Eval("Title") %>
<a href="view.aspx?ItemID=<%# Eval("Title") %>"><%# (Container.DataItem is TextItem) ? "read" : "watch" %></a>
</div>
<ItemTemplate>
</asp:Repeater>

You can also programmatically sort the data using anonymous methods as long as you sort on the shared fields:

// from above...
List<GenericItem> recentItems = GetRecentItems(); 
// re-sort on title
recentItems.Sort (delegate(GenericItem a, GenericItem b) { return a.Title.CompareTo(b.Title); });

I should note that there is quite a bit of JOINing, boxing, and unboxing going on here which means this may not be the best solution in all cases. But it is a helpful way of bringing together different kinds of data without needing to have one giant SQL table or one giant class.

Web Statistics