Embedded Youtube Videos with Placeholder Images

One of the requests I've had for a website I'm currently working on is to provide a placeholder image for an embedded Youtube Video.  Essentially, the client wants to display the image until someone clicks on it, then replace it with the video.  So here's how I did it with the new uTube package for Umbraco, but the same technique can be used for any web page...

With this method, I'm not trying to play the video in a separate window, or resize anything, I'm just wanting to have the video take the place of the image on the website.  This makes things a little easier, as I can render both the video and the image in separate div's and use CSS to lay one on top of the other using z-index and position elements.  In addition, I can use some fairly straight forward jquery javascript to hide one element and show the other.

Rendering a Flash Video player with an Image overlay

Here's a sample of the HTML including embedded javascript:

    <div id="mainFeature">
        <div id="mediaVideo" style="visibility: hidden">
            <div class="youTubePlayer" style="height: 433px; width: 680px;">
                <div id="ytPlayerAPI-rldN0jSBbZQ">
                    <p>
                        You need Flash version 8 and JS enabled to view the video</p>
                </div>
                <script type="text/javascript">
                    //SWF Embedd
                    var flashVars = { video_id: "rldN0jSBbZQ", playerapiid: "ytPlayerAPI-rldN0jSBbZQ", allowFullScreen: "true" }
                    var params = { allowScriptAccess: "always", wmode: "transparent", allowFullScreen: "true" };
                    var atts = { id: "ytPlayerAPI-rldN0jSBbZQ" };
                    swfobject.embedSWF("http://www.youtube.com/v/rldN0jSBbZQ?fs=1&rel=0&enablejsapi=1&version=3&playerapiid=ytPlayerAPI-rldN0jSBbZQ", "ytPlayerAPI-rldN0jSBbZQ", "680", "433", "8", null, flashVars, params, atts);
                </script>
            </div>
        </div>
        <div id="mediaImage">
            <a href="/recipes/crispy-chinese-chicken-with-sichuan-salt-pepper" title="Crispy Chinese Chicken with Sichuan Salt & Pepper">
                <img src="/media/2245/crispy_chinese_chicken_01.jpg" title="Crispy Chinese Chicken with Sichuan Salt & Pepper"
                    alt="Crispy Chinese Chicken with Sichuan Salt & Pepper" /></a></div>
        <script type="text/javascript">

            $(document).ready(function () {
                $('div#mediaImage a').click(function () {
                    $('div#mediaVideo').css('visibility', 'visible');
                    $('div#mediaImage').fadeOut(1000);
                    return false;
                });
            });
      
        </script>
    </div>

And the corresponding CSS to initially lay out the elements:

#mediaVideo, #mediaImage {
  position: absolute;
  left: 0;
  top: 0;
}

#mediaImage {
  z-index: 10;
}

Notice that in this example I'm using the SWFObject library to render the flash player. For some reason I haven't fully explored yet, if you render the flash object using object tags, the flash player is rendered on top of the mediaImage instead of obeying the CSS rules and rendering the image div on top as it should.

The result? When the user clicks on the image, it fades out and is replaced by the flash object.

Displaying embedded Youtube Videos on an iPhone/iPad

The thing about iPhone and iPad is it doesn't yet support Flash.  However, if you use the object tags approach, the device is smart enough to recognise the Youtube player and use the built-in YouTube app in it's place.  This is a pretty cool feature, but doesn't appear to work if you use the SWFObject library approach above.  Only problem is, although iPhones etc support javascript, and will honour the code above, all you see is the following message:

Rendering Youtube on iPhone with SWFObject libraryRendering a Youtube embedded video with the SWFObject on iPhone...

And using the <object> tag approach:

Rendering Youtube on iPhone with object TagsRendering a Youtube embedded video with object tags on iPhone...

Clicking on the video launches the Youtube App.  The iPad plays the video in place in the website using the Youtube app, which is pretty cool, only launching a separate window if you view it in fullscreen mode.

The code for rendering the Youtube with object tags is as follows:

            <div class="youTubePlayer" style="height: 433px; width: 680px;">
                <object width="680" height="433">
                    <param name="movie" value="http://www.youtube.com/v/rldN0jSBbZQ?fs=1&rel=0" />
                    <param name="allowFullScreen" value="true" />
                    <param name="allowscriptaccess" value="always" />
                    <embed src="http://www.youtube.com/v/rldN0jSBbZQ?fs=1&rel=0" type="application/x-shockwave-flash"
                        allowscriptaccess="always" allowfullscreen="true" width="680" height="433" /></object></div>

The best of both worlds: Supporting iPhone and rendering placeholder Images...

It's a compromise, but since we can't get both apple devices and standard browsers to render the same thing, we need to use the HTTP_USER_AGENT string to conditionally render the different approaches.

Umbraco and the uTube plugin...

For those of us utilising the excellent new uTube plugin with Umbraco, the following XSLT code should do the trick - note that you'll have to render the Chrome player if you want Full screen mode, as the chromeless player doesn't support it.  See my earlier post about this: Enabling Alternate Media with the uTube Umbraco Package

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [
  <!ENTITY nbsp " ">
]>
<xsl:stylesheet
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:msxml="urn:schemas-microsoft-com:xslt"
  xmlns:umbraco.library="urn:umbraco.library"
  xmlns:Exslt.ExsltCommon="urn:Exslt.ExsltCommon"
  xmlns:Exslt.ExsltDatesAndTimes="urn:Exslt.ExsltDatesAndTimes"
  xmlns:Exslt.ExsltMath="urn:Exslt.ExsltMath"
  xmlns:Exslt.ExsltRegularExpressions="urn:Exslt.ExsltRegularExpressions"
  xmlns:Exslt.ExsltStrings="urn:Exslt.ExsltStrings"
  xmlns:Exslt.ExsltSets="urn:Exslt.ExsltSets"
  xmlns:atom="http://www.w3.org/2005/Atom"
  xmlns:yt="http://gdata.youtube.com/schemas/2007"
  xmlns:media="http://search.yahoo.com/mrss/"
  xmlns:uTube.XSLT="urn:uTube.XSLT"
  exclude-result-prefixes="msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath
  Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets atom yt media uTube.XSLT"> <xsl:output method="xml" omit-xml-declaration="yes"/> <xsl:param name="currentPage"/> <xsl:template match="/"> <!-- YouTube URL or VideoID --> <xsl:variable name="uTubeVideo" select="/macro/uTubeVideo" /> <xsl:variable name="allowFullScreen" select="/macro/allowFullScreen" /> <xsl:variable name="allowRelatedVideos" select="/macro/allowRelatedVideos" /> <xsl:variable name="jsPlayerApiId" select="/macro/jsPlayerApiId" /> <!-- Don't do anything unless we have a value in uTubeVideo --> <xsl:if test="/macro/uTubeVideo"> <xsl:variable name="uTubeVideoID" select="uTube.XSLT:GetVideoId($uTubeVideo)" /> <xsl:variable name="uTubeXML" select="uTube.XSLT:GetVideoData($uTubeVideoID, 60)"/> <xsl:variable name="uTubeRatio" select="uTube.XSLT:GetAspectRatio($uTubeVideoID)"/> <!-- Video Width --> <xsl:variable name="uTubeWidth"> <xsl:choose> <!-- If the Width is not set, then check if Height is available --> <xsl:when test="not(/macro/uTubeWidth) and /macro/uTubeHeight"> <!-- Use the Height to get the Width --> <xsl:value-of select="uTube.XSLT:GetVideoWidth(/macro/uTubeHeight, $uTubeRatio)" /> </xsl:when> <!-- If the Width is set, use it! --> <xsl:when test="/macro/uTubeWidth"> <xsl:value-of select="/macro/uTubeWidth" /> </xsl:when> <!-- Otherwise fall back on a default value --> <xsl:otherwise> <xsl:value-of select="number(480)" /> </xsl:otherwise> </xsl:choose> </xsl:variable> <!-- Video Height --> <xsl:variable name="uTubeHeight"> <xsl:choose> <!-- If the Height is not set --> <xsl:when test="not(/macro/uTubeHeight)"> <!-- Then use the Width to get the Height --> <xsl:value-of select="uTube.XSLT:GetVideoHeight($uTubeWidth, $uTubeRatio)" /> </xsl:when> <xsl:otherwise> <!-- Otherwise use the user-defined value --> <xsl:value-of select="/macro/uTubeHeight" /> </xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:variable name="allowFullScreenText"> <xsl:choose> <xsl:when test="$allowFullScreen = 1">true</xsl:when> <xsl:otherwise>false</xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:variable name="uTubeRel"> <xsl:choose> <xsl:when test="$allowRelatedVideos = 1">&rel=1</xsl:when> <xsl:otherwise>&rel=0</xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:variable name="enableJSApi"> <xsl:if test="$jsPlayerApiId != ''">&enablejsapi=1&playerapiid=<xsl:value-of select="$jsPlayerApiId" /></xsl:if> </xsl:variable> <div class="youTubePlayer" style="height:{$uTubeHeight}px; width:{$uTubeWidth}px;"> <xsl:choose> <xsl:when test="uTube.XSLT:AllowEmbed($uTubeVideoID) = true()"> <xsl:choose> <xsl:when test="contains(umbraco.library:RequestServerVariables('HTTP_USER_AGENT'), 'iPad') or contains(umbraco.library:RequestServerVariables('HTTP_USER_AGENT'), 'iPhone')"> <object width="{$uTubeWidth}" height="{$uTubeHeight}"> <param name="movie"
                   value="http://www.youtube.com/v/{$uTubeVideoID}?fs={$allowFullScreen}{$uTubeRel}{$enableJSApi}"></param> <param name="allowFullScreen" value="{$allowFullScreenText}"></param> <param name="allowscriptaccess" value="always"></param> <embed src="http://www.youtube.com/v/{$uTubeVideoID}?fs={$allowFullScreen}{$uTubeRel}{$enableJSApi}" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="{$allowFullScreenText}" width="{$uTubeWidth}" height="{$uTubeHeight}"></embed> </object> </xsl:when> <xsl:otherwise> <!-- Video is allowed to be embedded --> <div id="{concat('ytPlayerAPI-',$uTubeVideoID)}"> <p>You need Flash version 8 and JS enabled to view the video</p> </div> <!-- SWFObject call to Embed Chromless player --> <script type="text/javascript"> //SWF Embedd var flashVars = {video_id : "<xsl:value-of select="$uTubeVideoID"/>",
                                            playerapiid: "<xsl:value-of select="concat('ytPlayerAPI-',$uTubeVideoID)" />",
                                            allowFullScreen: "<xsl:value-of select="$allowFullScreenText"/>" } var params = {allowScriptAccess: "always", wmode: "transparent",
                                            allowFullScreen: "<xsl:value-of select="$allowFullScreenText"/>" }; var atts = { id: "<xsl:value-of select="concat('ytPlayerAPI-',$uTubeVideoID)" />" }; swfobject.embedSWF("http://www.youtube.com/v/<xsl:value-of
                                                    select="$uTubeVideoID" />?fs=<xsl:value-of
                                                    select="$allowFullScreen" /><xsl:value-of
                                                    select="$uTubeRel" />&enablejsapi=1&version=3&playerapiid=<xsl:value-of
                                                    select="concat('ytPlayerAPI-',$uTubeVideoID)" />","<xsl:value-of
                                                    select="concat('ytPlayerAPI-',$uTubeVideoID)" />", "<xsl:value-of
                                                    select="$uTubeWidth" />", "<xsl:value-of
                                                    select="$uTubeHeight"/>", "8", null, flashVars, params, atts); </script> </xsl:otherwise> </xsl:choose> </xsl:when> <xsl:otherwise> <!-- Video Not Allowed to be embedded --> <p>This video does not allow it to be embedded.</p> </xsl:otherwise> </xsl:choose> </div> </xsl:if> </xsl:template> </xsl:stylesheet>

Any questions, comments or suggestions, feel free to leave a comment...

Enjoy!

Enabling Alternate Media with the uTube Umbraco Package

The project I'm currently working on requires embedding youtube videos in the page. For that I've settled on the excellent uTube Umbraco package found over here (or visit the Umbraco package page on our.umbraco.org...).

Out of the box, the uTube package provides some excellent functionality - you can upload videos directly from the Media repository, skinnable player as well as the full youtube player, ready made macros for including content...

However, as the site doesn't yet have videos for all the articles, and there's no guarantee that the articles will do so in the future, I wanted to be able to provide an alternative image instead of the video if it was missing.

In addition, some videos were already uploaded to youtube, and uTube currently doesn't have an import datatype for the Media section, so I wanted to be able to provide another alternative to the user to specify a youtube url as well as be able to choose from the Media items.

So I wrapped up the uTube macro's in my own "wrapper" macro:

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp " "> ]>
<xsl:stylesheet
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:msxml="urn:schemas-microsoft-com:xslt"
  xmlns:umbraco.library="urn:umbraco.library" xmlns:Exslt.ExsltCommon="urn:Exslt.ExsltCommon" xmlns:Exslt.ExsltDatesAndTimes="urn:Exslt.ExsltDatesAndTimes" xmlns:Exslt.ExsltMath="urn:Exslt.ExsltMath" xmlns:Exslt.ExsltRegularExpressions="urn:Exslt.ExsltRegularExpressions" xmlns:Exslt.ExsltStrings="urn:Exslt.ExsltStrings" xmlns:Exslt.ExsltSets="urn:Exslt.ExsltSets" xmlns:CWS.Twitter="urn:CWS.Twitter" xmlns:Locator="urn:Locator" xmlns:tagsLib="urn:tagsLib" xmlns:BlogLibrary="urn:BlogLibrary"
  exclude-result-prefixes="msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets CWS.Twitter Locator tagsLib BlogLibrary ">


<xsl:output method="xml" omit-xml-declaration="yes"/>

<xsl:param name="currentPage"/>
    <xsl:variable name="mainFeatureId" select="/macro/mainFeatureId"/>
    <xsl:variable name="mainFeature" select="umbraco.library:GetXmlNodeById($mainFeatureId)"/>
<xsl:template match="/">

  <xsl:choose>
    <xsl:when test="$mainFeature/uploadedVideo != ''">
      <xsl:variable name="macro">
        <![CDATA[<?UMBRACO_MACRO macroAlias="uTube.ChromelessPlayer.media" mediaID="]]><xsl:value-of select="$mainFeature/uploadedVideo"/><![CDATA[" uTubeWidth="680" uTubeHeight="433"></?UMBRACO_MACRO>]]>
      </xsl:variable>
      
      <!-- Render the chromeless player macro -->
      <xsl:value-of select="umbraco.library:RenderMacroContent($macro, $currentPage/@id)" disable-output-escaping="yes"/>

    </xsl:when>
    <xsl:otherwise>
      <xsl:choose>
        <xsl:when test="$mainFeature/video != ''">
          <xsl:variable name="macro">
            <![CDATA[<?UMBRACO_MACRO macroAlias="uTube.ChromelessPlayer" uTubeVideo="]]><xsl:value-of select="$mainFeature/video"/><![CDATA[" uTubeWidth="680" uTubeHeight="433"></?UMBRACO_MACRO>]]>
          </xsl:variable>
          
          <!-- Render the chromeless player macro -->
          <xsl:value-of select="umbraco.library:RenderMacroContent($macro, $currentPage/@id)" disable-output-escaping="yes"/>
        
        </xsl:when>
        <xsl:otherwise>
          <xsl:if test="count($mainFeature/bannerMedia) > 0">
            <a href="{umbraco.library:NiceUrl($mainFeature/@id)}" title="{$mainFeature/@nodeName}"><img src="{umbraco.library:GetMedia($mainFeature/bannerMedia, 'false')/umbracoFile}" title="{$mainFeature/@nodeName}" alt="{$mainFeature/@nodeName}" /></a>
          </xsl:if>
        </xsl:otherwise>
      </xsl:choose>

    </xsl:otherwise>
  </xsl:choose>

</xsl:template>

</xsl:stylesheet>

Taking a leaf out of the uTube.ChromelessPlayer.media macro's book (which is in itself a wrapper for the uTube.ChromelessPlayer macro), I'm testing for the existence of the uploadedVideo property, which points at the Media Item containing the uTube uploaded media.  If that doesn't exist, I then check for the video property, which is the alternative uTube Single Video Picker (the user simply pastes in a Youtube url).  If that doesn't exist, we fallback on a placeholder image instead.

End result: Graceful media fallback on the web page.

uTube.ChromelessPlayer.media