Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

Soapbox Video Gadget

4.98/5 (41 votes)
16 Apr 20077 min read 1   737  
The power of MSN videos in your Windows Vista sidebar with Soapbox. Create and share your favorite video list

Screenshot - top.jpg

Introduction

When I first started sidebar gadget development1, I found gadgets cool and innovative. But the more I learned about them, the more I realized their usefulness. Gadget development provides a whole new platform, becoming more productive in ways unimagined. On the one hand, it's a mini web application which can host a web page, call a web service, and use AJAX to read an RSS feed. On the other, it can interact with the Windows API using the power of .NET languages. Wow! That's the best of both worlds, wouldn't you agree?

The most important factor is the unique way in which a gadget provides the interaction between the web, Windows and users. In this example, I try to utilize this, to give user the "Power of Videos" in the sidebar. Check the architecture below.

To hold your interest, here is how it looks in the sidebar.

Screenshot - 320x240withPic.jpg

About the Gadget: The Power of Videos

Before I start, I must warn you: this gadget is quite addictive. Indulge yourself only during your spare time.

There are a number of RSS Feeds available from the Soapbox on MSN Video Beta page which give you the contents of the videos uploaded by the users. Some of the more interesting ones are: "Most Popular Videos", "Most Rated videos", and "Most Recent Videos". There is even a feed for "Videos By Tag" available, which we will use for searching videos. The idea behind this is to use those regularly updated feeds to show the user the video of his choice on the sidebar. If you want to know the basic barebones required to create a gadget, please refer to my first article Daily Dilbert . This article is actually an extension of that first article; I try to explain only those extra features which can be used to refine a gadget further:

  1. Create a gadget which can play a video in the flyout
  2. How to take care of paging when you read an XML file
  3. Settings and Search Videos
  4. Add video to "Favorite List"
  5. Create, read, update, & delete local XML file from Sidebar Gadget (new)
  6. Some tips in general

The Architecture Overview

Screenshot - Architecturenew.jpg

Let's begin...

Create a Video Gadget

A standard XML file with the name Gadget.xml is required as shown below:

Screenshot - settingsxml.jpg

More information about this is available here.

The name of the gadget is "Soapbox @ MSN" (as seen above). The namespace is basically to group more than one gadget (reserved for future use), which you can write in "mynamespace". MinPlatformVersion. Required. The expected value is "1.0."

"Permission" controls the amount of permission in the gadget. "Full" permission is required if you want to access a webpage through the gadget. With these two files, you can deploy and test your gadget in the sidebar. The <permission> tags and <type> tags will be more flexible in future versions of gadget development.

Our Video Gadget Will Have These Files

Gadget.xml An XML file defining the gadget properties, including name, icon and description
main.html The Main HTML page
main.js The core code/script for the gadget
Settings.html Exposes gadget settings for the user to change
Settings.js Script for the Settings file
soapboxFlyout.html The HTML file which will be used in Flyout window
Icon, images, etc. For use in the gadget selection box

You can get more details on these here.

Get the RSS Feed

The Main.html file will have five elements ("DIV") to get the feed data: cell0, cell1, etc. We can show the videos as a list of five videos (below) or single video.

Screenshot - main.jpgScreenshot - listview.jpg

The Feed for the Gadget

We have the URL which we call using the MSXML2.XMLHTTP object. This is the core of the AJAX until the feed gets loaded. Here is the magical JavaScript for this:

JavaScript
function getRSS() 
{
    document.getElementById("mylogo").Title = 
        System.Gadget.Settings.
        read("feedText");

    try
    {
        error.style.visibility = "hidden";

        loading.style.visibility = "visible";
        rssObj = new ActiveXObject("Msxml2.XMLHTTP");
        rssObj.open("GET",
            System.Gadget.Settings.read("videoFeed") , true);
        rssObj.onreadystatechange = function() {
            if (rssObj.readyState = == 4)
            { if (rssObj.status == 200) 
            { loading.innerText = ""; 
            error.innerText = ""; 
            error.style.visibility= "hidden"; 
            loading.style.visibility = "hidden"; 
            rssXML = rssObj.responseXML; 
            page = 0; 
            parseRSS();
            if 
                (chkConn) { setInterval(getRSS, 60 * 60000); 
            loading.style.visibility = "hidden";
                }
            } 
            else 
            {
                var chkConn;
                loading.style.visibility = "visible";
                chkConn = setInterval(getRSS, 30 * 60000);
            }
            } else {
                loading.style.visibility = "visible";
            }
        }    
        rssObj.send(null);
    }
    catch(err)
    {
        loading.style.visibility = "hidden";
        error.innerText = "   Service not available";
        error.style.visibility = "visible";
    }
}

Service Not Available

As you can see, if there is an error while connecting the feed or an error in the internet connection, we want to show the "Service not available" screen.

Screenshot - serviceNot.jpg

Parse the XML File

Here is the portion of the RSS Feed for a single item in the XML file and the corresponding "partial" ParseXML file to give you an idea of what we are doing with the feed.

Screenshot - feeddetail.jpg

JavaScript
function parseRSS(page) 
{
    rssItems = rssXML.getElementsByTagName("item");
    rssTitle = null; rssAuthors = null; rssDescription = null; 
    rssLink = null;
            
    rssTitle = escape(rssItems[i].firstChild.text);
    rssLink =rssItems[i].getElementsByTagName("guid")[0]
    .firstChild.nodeValue;
    rssViews = rssItems[i].getElementsByTagName("vidAt:playCount")[0]
    .firstChild.nodeValue;
    rssDateUpdate = rssItems[i].getElementsByTagName("vidAt:pubDate")[0]
    .firstChild.nodeValue;
    rssRatingValue = rssItems[i].getElementsByTagName("vidAt:rating")[0]
    .firstChild.nodeValue;
    rssDuration = rssItems[i].getElementsByTagName("vidAt:duration")[0]
    .firstChild.nodeValue;
    rssVDescription= escape(rssItems[i].getElementsByTagName
    ("vidAt:description")[0].firstChild.nodeValue);
    rssDescription=rssItems[i].getElementsByTagName("description")[0]
    .firstChild
    .nodeValue;
    myTitle = unescape(rssTitle)
    myTitle1 = myTitle.substr(0,16);
    myRatingTip = "Rating: " + rssRatingValueFormated2 + ", 
        View: " 
    + rssViews + ", Duration: " + rssDurationFormated ;
    
    document.getElementById("cell" + (cell)).innerHTML = 
        "onclick="
    showFlyout(\'' + rssTitle + '\',\'' + rssVDescription + '\',
    \'' + rssLink + '\',\'' + rssImage + '\',\'' + rssRImage + '\',\'' 
    + rssViews + '\',
    \'' + rssDateUpdate + '\',\'' + rssDurationFormated + '\',
    \'' + rssRatingValueFormated2 + '\');" >
}

Since we have the feed required, we can create a flyout from the video. This is the showFlyout function, which is created dynamically and is used to pass all the parameters from the item to the flyout page.

JavaScript
function showFlyout(sTitle,sDescription,sGuid, sImage, sRImage,sView,
    sDateUpdate,sDuration,sRatingFormated)
{

    if (System.Gadget.Settings.read("sTitle")==sTitle)
    {
    System.Gadget.Settings.write("sTitle", "myTitle");
    hideFlyout();
    }
    else
    {
        System.Gadget.Settings.write("sTitle", sTitle);
        System.Gadget.Settings.write("sView", sView);
        System.Gadget.Settings.write("sDateUpdate", sDateUpdate);
        System.Gadget.Settings.write("sGuid", sGuid);
        System.Gadget.Settings.write("sDescription", sDescription);
        System.Gadget.Settings.write("sImage", sImage);
        System.Gadget.Settings.write("sRImage", sRImage);
        System.Gadget.Settings.write("sRatingFormated", 
            sRatingFormated);
        System.Gadget.Settings.write("sDuration", sDuration);
        System.Gadget.Flyout.file = "soapboxFlyout.html";
        System.Gadget.Flyout.show = true;
    }
}

Other Settings for the Gadget

Apart from the settings which are set on the showFlyout function, there are other settings which can be managed by the user. The user can search under "Video" or select from one of the Video feeds.

Once we have all the settings, we Build the video object in the flyout window.

JavaScript
function BuildVideoObject()
{
    try
    {
        if (System.Gadget.Settings.read("videoSize") 
            =="large")
        {
        Video_HtmlString  = '‹OBJECT id="VIDEO" 
            width="640" height="480" '; 
        }
        else 
        {
        Video_HtmlString  = '‹OBJECT id="Body1" 
            width="320" height="240" '; 
        }    
    Video_HtmlString += 'style="position:absolute; left:0;top:0;"';
    Video_HtmlString += 'CLASSID="CLSID:6BF52A52-394A-11d3-B153-"';
    Video_HtmlString += 'type="application/x-oleobject" 
        VIEWASTEXT›';
    Video_HtmlString += '‹PARAM NAME="URL" VALUE="';
    Video_HtmlString += 
        "http:/soapbox.msn.com/StreamingUrl.aspx?vid=" 
    + System.Gadget.Settings.read("sGuid");
    Video_HtmlString += '"›‹param NAME="stretchToFit" 
        VALUE="1"›';
    Video_HtmlString += '‹param name="AutoSize" 
        value="1"›
    ‹PARAM NAME="SendPlayStateChangeEvents" 
        VALUE="True"›';
    Video_HtmlString += '‹PARAM NAME="AutoStart" 
        VALUE="True"›';
    Video_HtmlString += '‹PARAM name="uiMode" 
        value="none"›';
    Video_HtmlString += '‹PARAM name="PlayCount" 
        value="9999"›';
    Video_HtmlString += '‹/OBJECT›';
    document.write(Video_HtmlString);
    }
    catch (err)
    {
     document.write("Problem with the machine's Windows Media 
         Player");
    }
} 

More information on the flyout is available here.

Paging

Since XML files can have hundreds of items, we need to be able to page it. So we add a DIV item in the main.html file with the following:

Screenshot - tab.jpg

JavaScript
‹div id="tbar" title=""›
‹table width="100%" height="100%"  border="0" cellpadding="2" cellspacing="0" 
ID="Table2"›
‹tr align="center" valign="middle"›
    ‹td width="12" class="arrow" onclick="chPage(-999);" align="right"›
    «‹/td›
    ‹td width="12" class="arrow" onclick="chPage(-1);" align="right"›
    ‹b›‹‹/b›‹/td›
    ‹td class="sub" id="pageNum"›1/1‹/td›
    ‹td width="12" class="arrow" onclick="chPage(+1);" align="left"›
    ‹b››‹/b›‹/td›
    ‹td width="12" class="arrow" onclick="chPage(+999);" align="left"›
    »‹/td›
‹/tr›
‹/table›
‹/div›

A Change page function will Parse the content of items to that page.

JavaScript
function chPage(off) 
{
    try
    {

        if (System.Gadget.Settings.read("listview"))
        {
            if (rssItems.length < 100) 
            {
                myval = Math.ceil (rssItems.length/5);
            }
            else
            {myval = 20;}
        }
        else
        {
            if (rssItems.length < 100) 
            {
                myval = rssItems.length;
            }
            else
            {myval = 100;}
        }


        if (off === -999)
        {
            off = myval-page;
        }
        if (off === 999)
        {
            off = myval-page-1;
        }

        try
        {
            if (((page + off) > -1) && ((page + off) < myval)) 
            {
                page = page + off;
                parseRSS(page);
            } else if ((page + off) === myval) 
            {
                page = 0;
                parseRSS(page);
            } else if ((page + off) === 0) 
            {
                page = myval;
                parseRSS(page);
            }
        }
        catch (err)
        {
        }
    }
    catch (err)
    {
    }
}

Make Your Own Favorite Videos List

Screenshot - addtofavorite.jpg

Now with each video, you will see a plus icon in the bottom, so you can add the video to your favorites list.

A Portion of the Code Behind

JavaScript
function addtoFavorites(sGuid)
{

  var variableName = "userprofile";
  var mytext ="Testing";
   
  try
            {       
   var variableName = "userprofile";
   xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
   xmlDoc.async="false";
   xmlDoc.load(System.Environment.getEnvironmentVariable
   (variableName) + "\\Videos\\SoapBoxFavorites.xml");
   xmlObj=xmlDoc.documentElement;
   rssAddItems = xmlObj.getElementsByTagName("channel");
   rssAddedItems= xmlObj.getElementsByTagName("item");
   var varAlreadythere = false;


  if (rssItems[i].getElementsByTagName("guid")[0]
     .firstChild.nodeValue == sGuid)
    {
    if (!varAlreadythere)
     {
       rssAddItems[0].appendChild(rssItems[i]);
      }
     break;
     }
                  
xmlDoc.save(System.Environment.getEnvironmentVariable
(variableName) + "\\Videos\\SoapBoxFavorites.xml");

Screenshot - addedinfavorite.jpg

When you click on the "Add to Favorite" icon, it will add the XML feed information for that video from the online feed to a local XML file.

%userprofile%/Video/soapboxfavorites.xml

Here are the new settings to view your favorite videos. Select "My Favorite Videos" to see the bookmarked videos.

Screenshot - newsetting.jpg

If any "My Favorites" are selected, the gadget will read from the local XML file.

A Portion of the Code Behind

JavaScript
if (System.Gadget.Settings.read("feedText").indexOf("My favorite") >=0)
{
try
{
 var variableName = "userprofile";
 var xmlDoc = new ActiveXObject("Msxml2.DOMDocument");
 xmlDoc.load(System.Environment.getEnvironmentVariable(variableName)
    + "\\Videos\\SoapBoxFavorites.xml");
 rssXML=xmlDoc;
 parseRSS();
 }

By default, the "My Favorite Video" list will show nine videos from the "Current Featured Feed".

Create the XML file for the first time from "Current Featured Feed".

A Portion of the Code Behind

JavaScript
var variableName = "userprofile"
var SoapboxFavoritesPath = System.Environment.getEnvironmentVariable
 (variableName) + "\\Videos\\SoapBoxFavorites.xml";

rssObj = new ActiveXObject("Msxml2.XMLHTTP");
rssObj.open("GET",System.Gadget.Settings.read("videoFeed"),true);
rssObj.onreadystatechange = function()
{
if (rssObj.readyState === 4)
{
if (rssObj.status === 200)
{
try
 {
  System.Shell.itemFromPath(SoapboxFavoritesPath);
  return;
 }
 catch (notFound)
 {
 var stream = new ActiveXObject("ADODB.Stream");
 stream.Type = 1;
 stream.Open();
 stream.Write(rssObj.responseBody);
 stream.SaveToFile(SoapboxFavoritesPath, 2);
 stream.Close;
 stream = null;
 }

You can also remove the video from the Video list. Select My Favorite video, and then you can see the "Remove icon" after each video in the gadget.

Screenshot - removefromfavorites.jpg

The Code Behind Remove

JavaScript
function removefromFavorites(sGuid)
{
   var variableName = "userprofile";
   var mytext ="Testing";
               
   xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
   xmlDoc.async="false";
   xmlDoc.load(System.Environment.getEnvironmentVariable
   (variableName) + "\\Videos\\SoapBoxFavorites.xml");
   xmlObj=xmlDoc.documentElement;
   rssRemoveItems = xmlObj.getElementsByTagName("item");

     if (rssRemoveItems[i].getElementsByTagName("guid")
         [0].firstChild.nodeValue==sGuid)
        {
        var lastNode=rssRemoveItems[i];
        var delNode=xmlObj.firstChild.removeChild(lastNode);
        xmlDoc.save(System.Environment.getEnvironmentVariable
          (variableName) + "\\Videos\\SoapBoxFavorites.xml");
        mytext = "Deleted and Saved";
        break;
        }
 }

And we are done!

Tips in General

  • To resize a windows media player to a custom width, set the ‹param name="AutoSize" value="1"›
  • Any exceptions in a sidebar gadget main window should be handled with the "Service Not Available" screen, with information icon as shown above
  • Whenever you use a JavaScript function, encapsulate it inside a try catch statement to avoid surprises
  • Escape(^) and unescape(^) are nice little JavaScript functions for URL encoding and un-encoding when you need to pass URLs, etc. as parameters to the flyout window
  • The HTML code for < is "&laquo;" , << is "&lsaquo;" , >> is "&rsaquo;" and > "&raquo;"
  • To check whether a file exists in a local folder, use System.Shell.itemFromPath(Path) inside try catch
  • For a timer functionality, use the setinterval4 method
  • For multiple language support, create a folder for that language (e.g. 'IT-CH', if the language is it-ch Italian Switzerland) and copy all files from the 'EN-US' folder to the new created folder (language codes ^)
  • Check design concepts and usage pattern5

In Action

Soapbox Gadget in Action: Size: 400 X 360 in flash player

Screenshot - FLASH.jpg

References

  1. Daily Dilbert : A Beginner's Gadget
  2. Soapbox at MSN Videos
  3. MicrosoftGadgets.com sidebar development
  4. Windows setinterval
  5. Design concepts and usage pattern

Article History

  • Mar 07, 2007: First published
  • Mar 08, 2007: Added support for flash player
  • Mar 09, 2007: Changed "check for updated feed" 30 minutes
  • Mar 11, 2007: Added architecture diagram
  • Mar 20, 2007: Create "My Favorite Videos"
  • Mar 23, 2007: Save your favorite video list to share
  • Mar 23, 2007: Open shared favorite video list (added sample)
  • Apr 02, 2007: Updated search string
  • Apr 11, 2007: Updated & added some defaults

And Thanks

For coming so far! I hope you find this useful, and give me a vote/comment if you do and take care.

License

This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.

A list of licenses authors might use can be found here.