65.9K
CodeProject 正在变化。 阅读更多。
Home

Soapbox 视频小工具

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.97/5 (33投票s)

2007 年 3 月 7 日

7分钟阅读

viewsIcon

200628

downloadIcon

753

将 MSN 视频的强大功能集成到您的 Windows Vista 边栏中,尽在 Soapbox。创建和分享您最喜爱的视频列表

Screenshot - top.jpg

引言

当我刚开始边栏小工具开发1时,我觉得小工具很酷且富有创新性。但随着我对它们了解越多,我越意识到它们的实用性。小工具开发提供了一个全新的平台,以意想不到的方式提高了生产力。一方面,它是一个可以托管网页、调用 Web 服务并使用 AJAX 读取 RSS 源的迷你 Web 应用程序。另一方面,它可以通过 .NET 语言的力量与 Windows API 进行交互。哇!这是两全其美,您同意吗?

最重要因素是小工具在 Web、Windows 和用户之间提供交互的独特方式。在此示例中,我试图利用这一点,赋予用户边栏中的“视频力量”。请查看下面的架构。

为了保持您的兴趣,这是它在边栏中的样子。

Screenshot - 320x240withPic.jpg

关于小工具:视频的力量

在我开始之前,我必须警告您:这个小工具非常容易上瘾。只在您的空闲时间沉迷其中。

MSN 视频 Beta 页面上的 Soapbox 上有许多可用的 RSS 源,它们提供了用户上传的视频内容。一些比较有趣的包括:“最受欢迎的视频”、“评分最高的视频”和“最新视频”。甚至还有一个“按标签搜索视频”的源,我们将用它来搜索视频。其背后的想法是使用这些定期更新的源来向用户显示边栏上的他/她选择的视频。如果您想知道创建小工具所需的基本要素,请参阅我的第一篇文章 Daily Dilbert。这篇文章实际上是那篇第一篇文章的扩展;我只尝试解释那些可以进一步改进小工具的额外功能。

  1. 创建一个可以在弹出窗口中播放视频的小工具
  2. 如何处理读取 XML 文件时的分页
  3. 设置和搜索视频
  4. 将视频添加到“收藏夹列表”
  5. 从边栏小工具创建、读取、更新和删除本地 XML 文件(新)
  6. 一些通用技巧

架构概述

Screenshot - Architecturenew.jpg

让我们开始...

创建视频小工具

需要一个名为 Gadget.xml 的标准 XML 文件,如下所示

Screenshot - settingsxml.jpg

有关此内容的更多信息,请在此处 获取

小工具的名称是“Soapbox @ MSN”(如上所示)。命名空间基本上是为了将多个小工具(保留供将来使用)分组,您可以将其写入“mynamespace”。MinPlatformVersion. Required. 预期值为“1.0。”

“Permission”控制小工具的权限级别。“Full”权限如果您想通过小工具访问网页是必需的。有了这两个文件,您就可以在边栏中部署和测试您的小工具了。<permission> 标签和 <type> 标签在未来版本的小工具开发中会更加灵活。

我们的视频小工具将包含以下文件

Gadget.xml 一个 XML 文件,定义小工具的属性,包括名称、图标和描述
main.html 主 HTML 页面
main.js 小工具的核心代码/脚本
Settings.html 暴露小工具设置供用户更改
Settings.js 设置文件的脚本
soapboxFlyout.html 将在弹出窗口中使用 HTML 文件
图标、图像等 用于小工具选择框

您可以在此处 获取 更多关于这些的详细信息。

获取 RSS 源

Main.html 文件将包含五个元素(“DIV”)来获取源数据:cell0cell1 等。我们可以将视频显示为五个视频的列表(如下所示)或单个视频。

Screenshot - main.jpgScreenshot - listview.jpg

小工具的源

我们有一个 URL,我们使用 MSXML2.XMLHTTP 对象进行调用。这是 AJAX 的核心,直到源加载为止。这是神奇的 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";
    }
}

服务不可用

如您所见,如果在连接源时出现错误或互联网连接出现错误,我们希望显示“服务不可用”屏幕。

Screenshot - serviceNot.jpg

解析 XML 文件

这是 XML 文件中单个项目的 RSS 源的一部分,以及相应的“部分”ParseXML 文件,让您了解我们如何处理源。

Screenshot - feeddetail.jpg

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 + '\');" >
}

既然我们有了所需的源,我们就可以从视频创建弹出窗口。这是 showFlyout 函数,它是动态创建的,用于将所有参数从项目传递到弹出窗口页面。

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;
    }
}

小工具的其他设置

除了在 showFlyout 函数上设置的设置外,还有其他设置可以由用户管理。用户可以在“视频”下搜索或从其中一个视频源中选择。

一旦我们获得所有设置,我们就会在弹出窗口中 Build video 对象。

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");
    }
} 

有关弹出窗口的更多信息,请在此处 获取

分页

由于 XML 文件可能包含数百个项目,我们需要能够分页。因此,我们在 main.html 文件中添加了一个 DIV 项,内容如下

Screenshot - tab.jpg

‹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›

更改页面函数将解析内容的项目到该页面。

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)
    {
    }
}

制作您自己的收藏视频列表

Screenshot - addtofavorite.jpg

现在,每个视频的底部都会有一个加号图标,您可以将视频添加到您的收藏夹列表。

部分后台代码

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

当您单击“添加到收藏夹”图标时,它会将该视频的 XML 源信息从在线源添加到本地 XML 文件中。

%userprofile%/Video/soapboxfavorites.xml

这里是查看您收藏视频的新设置。选择“我的收藏视频”以查看书签视频。

Screenshot - newsetting.jpg

如果选择了任何“我的收藏”,小工具将从本地 XML 文件中读取。

部分后台代码

 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();
  }

默认情况下,“我的收藏视频”列表将显示来自“当前精选源”的九个视频。

首次从“当前精选源”创建 XML 文件。

部分后台代码

 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;
  } 

您也可以从视频列表中删除视频。选择我的收藏视频,然后您会在小工具中看到每个视频后面的“删除图标”。

Screenshot - removefromfavorites.jpg

删除的后台代码

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;
        }
 }

这样我们就完成了!

通用技巧

  • 要将 Windows Media Player 调整为自定义宽度,请设置 ‹param name="AutoSize" value="1"›
  • 边栏小工具主窗口中的任何异常都应通过“服务不可用”屏幕和如图所示的信息图标进行处理
  • 每当您使用 JavaScript 函数时,请将其封装在 try catch 语句中,以避免意外
  • Escape(^)和 unescape(^)是 URL 编码和解码的有用 JavaScript 函数,当您需要将 URL 等作为参数传递给弹出窗口时
  • < 的 HTML 代码是“&laquo;”,<< 是“&lsaquo;”,>> 是“&rsaquo;”,而 > 是“&raquo;
  • 要检查文件是否存在于本地文件夹中,请在 try catch 中使用 System.Shell.itemFromPath(Path)
  • 要实现计时器功能,请使用 setinterval4 方法
  • 要支持多语言,请为该语言创建一个文件夹(例如,'IT-CH',如果语言是 it-ch 意大利语-瑞士),并将 'EN-US' 文件夹中的所有文件复制到新创建的文件夹中(语言代码 ^
  • 查看设计概念和使用模式5

实际应用

Soapbox 小工具实况:尺寸:400 X 360(Flash 播放器)

Screenshot - FLASH.jpg

参考文献

  1. Daily Dilbert:初学者小工具
  2. Soapbox 在 MSN 视频
  3. MicrosoftGadgets.com 边栏开发
  4. Windows setinterval
  5. 设计概念和使用模式

文章历史

  • 2007 年 3 月 7 日:首次发布
  • 2007 年 3 月 8 日:增加了对 Flash 播放器的支持
  • 2007 年 3 月 9 日:“检查更新的源”时间间隔更改为 30 分钟
  • 2007 年 3 月 11 日:添加了架构图
  • 2007 年 3 月 20 日:创建“我的收藏视频”
  • 2007 年 3 月 23 日:保存您的收藏视频列表以供分享
  • 2007 年 3 月 23 日:打开共享的收藏视频列表(已添加示例)
  • 2007 年 4 月 2 日:更新了搜索字符串
  • 2007 年 4 月 11 日:更新并添加了一些默认值

感谢

感谢您一路阅读!希望您觉得这篇文章有用,如果您觉得有用,请给我投票/评论,并请多保重。

许可证

本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。

作者可能使用的许可证列表可以在此处找到。

© . All rights reserved.