YouTube Video Mixer using Resolume
by Eric Medine aka MKultra

This is a quick walkthru of my process, from concept to completion, of how to build a flash utility that will enable chromeless playback of any YouTube video from a standard YouTube URL (no interface, borders, etc-- just a video feed in the FLV video format). The final version of this is to be enabled to run thru the video mixing software Resolume, but you should easily be able to modify it for whatever you need.

I used a Dell Inspiron 8100 with 2 gigs of ram running WinBlows XP, some kinda ATI video card that has disappointed me in the past, and a 2.4 MHZ processor. Running Vista? Haven't tried it yet.

About this tutorial
Use this tutorial however you want, feel free to add to it, be sure to forward it to whomever and give it away for free. If you charge some lazy bastard for this tutorial, you suck. If you use it for a performance and you make money-- you rock! If you have some questions, advice, or praise, contact me at:
info(at)ericmedine(dot)com

Other resources:
Here's some links to related projects, code libraries, and some other resources that I used.
Thread on DennisJaamann.org about a similar project using Flex
FlashLoaded.com has some posts about converting youTube urls to XML
The Resolume.com manual has fairly detailed instructions for importing Flash AS3 animation into resolume.


STEPS:
1) Convert a YouTube web address into a playable/downloadable FLV
2) Import the FLV into Flash/ Resolume
 

When you look at a video on the YouTube, you'll notice that the header of the HTML page that holds it has a variable at the end. For instance, if I want to see the 1,500 prisoners of Cebu Provincial Detention and Rehabilitation Center in the Phillipines do their version of "Thriller", the location for that is: "http://www.youtube.com/watch?v=hMnk7lh9M3o", where "?v=hMnk7lh9M3o" is the variable that tells the YouTube website which video to display. However, this variable is not useful when trying to find, edit, or manipulate a video from YouTube on its own. Why would you want to do this, you ask? In my case, I use YouTube to as a batch compression server for my FLV's, but I need to manipulate them without the rest of the YouTube html page.

If you don't know what YouTube is, you're in the wrong place. If you're unsure of how to deal with Flash, loading FLV's, and dowloading/streaming video, I recommend doing some Google searches and checking out some tutorials. This walkthru assumes a familiarity with writing PHP and Actionscript code, and uploading and downloading files as well.

Basically , we want to automate the functionality of such sites as http://kej.tw/flvretriever/ to get the actual video file, which is saved in the FLV format. Then, embed the video into Flash and play it back.

So-- how do we get the FLV?

STEP ONE: Convert a YouTube header to a FLV

A. First, find the VIDEO ID.

If you go to the page in question (for this example, "http://www.youtube.com/watch?v=hMnk7lh9M3o") and view the source HTML ("view>source"), you'll get a bunch of code you don't need, as well as the code that embeds the flash movie in the HTML page itself-- this code is contained within the "swfArgs" tag. It should look something like the code below:

var swfArgs = {
"q": "top%20gear%20carrera%20gt",
"fexp": "903500,900130", 
"invideo": true, 
"sourceid": "ys",
"video_id":"vE_WqdKbTvY", 
"l": 478,
"fmt_map": "18/512000/9/0/115,34/0/9/0/115,
5/0/7/0/0", 
"sk": "PbB4AIyvqpbLl0DYyCX5rpmXcQe-FEcZC", 
"is_doubleclick_tracked": "1", 
"usef": 0,
"t": "vjVQa1PpcFO6TnxVA4_nkqbqKN-z4CoWJgWn2
Pfu77I=", 
"hl": "en", 
"plid": "AARoEenKN8A9FYLn", 
"vq": null, "ad_module": 
"http://s.ytimg.com/yt/swf/ad-vfl91517.swf"
, "cr": "NL", "tk":
"j7SixOLxSJ1xxBIwWubnN9sXiu7hrejTmRv4ruMx4N3 OaeyhN0xImQ=="
};

As you can see, this is an object containing data about the swf and thus the .flv played at the youtube html page. The video_id is the identifier of the movie and t is a token set by youtube that enables you to view or download the video. This token expires after a given period of time, so it's not possible to put a permanent download link for the flv file here.

So-- the trick now is, how do we easily get the id and token #s for any YouTube URL, without having to look at all that code? Answer: PHP!

<?php
$url=trim($_REQUEST['url']);

$ch=curl_init();

curl_setopt($ch,CURLOPT_URL,$url);
curl_setopt($ch,CURLOPT_HEADER,false);
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);

$info=curl_exec($ch);

preg_match('#"video_id":.*?"(.*?)"#is',$info,$matches);
$video_id=$matches[1];

preg_match('#"t":.*?"(.*?)"#is',$info,$matches);
$tag_t=$matches[1];

/*
$pos1=strpos($info,'"video_id":"');
$pos2=strpos($info,'"t":"');

$video_id=substr($info,$pos1+13);
$video_id=substr($video_id,0,strpos($video_id,'",'));

$tag_t=substr($info,$pos2+6);
$tag_t=substr($tag_t,0,strpos($tag_t,'",'));
**/


$response='<video>';
$response.='<id>'.$video_id.'</id>';
$response.='<t>'.$tag_t.'</t>';
$response.='</video>';

header("Content-type:text/xml");

echo$response;

curl_close($ch);
?>




I've found a super helpful script from FlashLoaded.com that does the trick-- basically, you pass the YouTube URL into a PHP file that you host on your server (more on that later), and it returns an XML string that holds the id that we need. Once we get that, we can use that to find the FLV to play in flash.

So, make a new PHP page with the code on the left, and put it on your server at a place where you can find it later. For this example, I saved this file at my server as: "http://ericmedine.com/temps/youtube/convertURL.php"

<video>
<id>
hMnk7lh9M3o</id>
<t>
vjVQa1PpcFOx- qiA9bHuJ4lisoLWL9PCn0RFQu91Ic%3D</t>
</video>

Now, if we want to get the video id of our Philipino dancers, just pass the YouTube URL into the PHP file we just created by adding a "?url=yourYouTubeUrl" in the header, right after the path to the PHP file itself.
Like so: "http://ericmedine.com/temps/youtube/convertURL.php?url= http://www.youtube.com/watch?v=hMnk7lh9M3o"

Immediately you should get an XML file that looks something like this on the left. You'll notice it has the video ID (hMnk7lh9M3o) and the token (vjVQa1PpcFOx- qiA9bHuJ4lisoLWL9PCn0RFQu91Ic%3D) that we need to do our thing.

Now, all I have to do is take out that video ID, and turn it into a path that will take us to the actual FLV that resides on the youTube servers!

How do I do that, you ask? More PHP!

B) Use the VIDEO ID to find a video file playable by Flash
I've found a super helpful script from DennisJaamann.org that does exactly what we want-- basically, you pass the video ID into a PHP file that you host on your server and it returns a path to the FLV file on the YouTube server.
<?php
    $id = trim($_REQUEST['id']);
    $url = "http://www.youtube.com/watch?v=" . $id;
    $url = $url . "&fmt=18"; //Gets the movie in High Quality
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_HEADER, false);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $info = curl_exec($ch);
    if (!preg_match('#var swfArgs = (\{.*?\})#is', $info, $matches))
    {
        echo "Check the YouTube URL : {$url} <br/>\n";
        die("Couldnt detect swfArgs");
    }
    if (function_exists(json_decode)) # >= PHP 5.2.0
    {
	$swfArgs = json_decode($matches[1]);

        $video_id = $swfArgs->video_id;
        $t = $swfArgs->t;

    }
    else
    {
        preg_match('#"video_id":.*?"(.*?)"#is', $matches[1], $submatches);
        $video_id = $submatches[1];
        preg_match('#"t":.*?"(.*?)"#is', $matches[1], $submatches);
        $t = $submatches[1];
    }
    curl_close($ch);
    $fullPath = "http://www.youtube.com/get_video.php?video_id=" . $video_id . "&t=" . $t; 
// construct the path to retreive the video from
$headers = get_headers($fullPath); // get all headers from the url foreach($headers as $header){ //search the headers for the location url of youtube video if(preg_match("/Location:/i",$header)){ $location = $header; } } header($location); // go to the location specified in the header and get the video ?>

So, make a new PHP page with the code at the left, and put it on your server at a place where you can find it later. For this example, I saved this file at my server as: "http://ericmedine.com/temps/youtube/getFLV.php"

Now, if we want to get path to the actual FLV of our Philipino dancers, just pass your URL into the PHP file like so:
"http://ericmedine.com/temps/youtube/getFLV.php?id=hMnk7lh9M3o"

And that's it-- you should get the popup window to download your FLV!

Unfortunately, we don't want to be copy and pasting stuff all day every time we want to find a video, so let's build our call to the PHP from inside Flash and have it run all automatically!

STEP TWO: Import our FLV path into Flash

Now that we have the path to the FLV, it's quite easy to load it into flash. We'll be using the XML object to get our video ID, and the NetStream object to handle our video.

First, make a new FLA file (I called mine "youTubePlayer.FLA") and create a classpath ("youTubePlayer.as"). Create a video object from the library, drop it on the stage, and call it "theVideoHolder". In the "youTubePlayer.as" file, create your ActionScript package, import your media (remember, you're going to need to use XML, get URL, video objects, etc), and put the following actionscript code into it. If you are unsure how to build a video player that can handle a NetStream or NetConnection, or how the XML object works, there's plenty of online tutorials that will point you in the right direction.

If you want to make this some kind of stand-alone widget, you'll want to make your import parameters from an input text box or something. In the case of Resolume, however, you will rely on the ResolumeCom classes to handle your input from the Resolume interface. If you're unsure of how Resolume handles Flash parameters, I refer you to their online manual which should familiarize you with how it handles Flash and ActionScript:
http://docs.google.com/Doc?id=dhjs2rsx_0g87srqfr&hl=en#Flash

Quick overview: Resolume includes a set of class files that you compile when you export a SWF for use with their video mixing platform. Although they do a lot of stuff behind the scenes, we're only concerned with the ability to pass data back and forth from Resolume to our Flash movie (which in turn will pass data to our PHP files). Basically Resolume handles three types of input-- Strings, Boolean (true/false), and FLoat (number values). You create the vars that will hold these values, attach an event listener to them, and Rez automatically puts them in its video mixing panel and attaches them to whatever input you want!

package {

import flash.display.MovieClip;
import flash.events.*;
import flash.xml.*;
import flash.net.*;
import flash.text.*;
import flash.media.Video;

///
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.net.URLRequestHeader;
import flash.net.URLRequestMethod;
import flash.net.URLVariables;
import flash.net.navigateToURL;

import resolumeCom.*;
import resolumeCom.parameters.*;
import resolumeCom.events.*;

public class youTubePlayer extends MovieClip
{
//// init video player
var nsVideo:NetStream;
var vFLV:Video;
var ncVideo:NetConnection
var theVideo:String;
public var myYoutubeURLReturn;

private var resolume:Resolume = new Resolume();

///// this is the text field that you type the youtube URL into
///// when the textfield changes, it launches the parameter changed // function and
// converts the URL to an FLV

private var paramText:StringParameter = resolume.addStringParameter("YouTube URL", "default");

public function youTubePlayer():void{
resolume.addParameterListener(parameterChanged);
}

public function parameterChanged(event:ChangeEvent): void{
if (event.object == paramText) {
//// this sends the youtube URL to the PHP file that
/// will find the video ID, and return it as an XML file
var theVideoID:String = "http://www.ericmedine.com/temps/youtube/convertURL.php?url=" + this.paramText.getValue();
//// parse youtube url
parseURL(theVideoID);
} else {
trace(event.object);
}

}
//////////////////////////////////////////////////////////
////////////////// URL FUNCTIONS //////////////////////////////////
///////////////////////////////////////////////////////////////////

////// create a new xml object, then load it, and find the video ID
private function parseURL(theVideoID){
// trace("I should be XML" + theXML);
var xmlLoader:URLLoader = new URLLoader();
var xmlData:XML = new XML();
xmlLoader.addEventListener(Event.COMPLETE, LoadXML);
xmlLoader.load(new URLRequest(theVideoID));
}

private function LoadXML(e:Event):void {
var xmlData = new XML(e.target.data);
// trace("I am the current XML ID: " + xmlData.id);
initVideo(xmlData.id);

}

///////////////////////////////////////////////////////////////
///////////////// VIDEO FUNCTIONS ///////////////////////////////
///////////////////////////////////////////////////////////
private function initVideo(theVideo){
trace("I should be: " + theVideo);
var ncVideo:NetConnection = new NetConnection();
ncVideo.connect(null);
nsVideo = new NetStream(ncVideo);
nsVideo.addEventListener(NetStatusEvent.NET_STATUS, videoStatusHandler);
nsVideo.client = new Object(); /// this fixes the asynch error

//// uses a different PHP script to convert the video ID to a path to an FLV that the flash player can load
nsVideo.play("http://www.ericmedine.com/temps/youtube/getFLV.php?id=" + theVideo);
// vFLV = new Video();
// videoScreen.videoHolder.addChild(vFLV);
theVideoHolder.attachNetStream(nsVideo);
}

function videoStatusHandler(event:NetStatusEvent) {
if (event.info.code == "NetStream.Buffer.Empty") {
trace("I am empty!");
/// do something
} else if (event.info.code == "NetStream.Play.Stop"){
//// maybe you want to loop it?
trace("I am stopped!");
} else if (event.info.code == "NetStream.Buffer.Flush"){
//// do another thing
}
}

///// end class
}
/// end package
}

So-- since all we need is to import text (the YouTube URL), we're just going to use the standard "ParamText" variables that resolume so helpfully provides us with in their sample files. In this case, I want the label of the input text box that I type my YouTube url into to read "YouTube URL", so here it is:
private var paramText:StringParameter = resolume.addStringParameter("YouTube URL", "default");

As you may know (if you don't, I refer you to the Resolume web site), once you type some new text in the text box in the Resolume input, the ParameterChanged listener sends the value to the parameterChanged function, in a new variable that we've created called "theVideoID". We append this variable to the path of the PHP page we made earlier-- convertURL.php-- like so:
var theVideoID:String = "http://www.ericmedine.com/temps/youtube/convertURL.php?url=" + this.paramText.getValue();

Make sense? "theVideoID" now will be the result of our YouTube URL sent to the PHP page ("convertURL.php") that finds the video ID-- so the value of "theVideoID" is an XML string that holds the video ID.

Flash then sends the value of "theVideoID" (the XML string containing the video ID) to the XML object using the parseURL function like so:
parseURL(theVideoID);

The parseURL function takes the "theVideoID" data, extracts the video ID from it, and sends it to our video player as follows:
initVideo(xmlData.id);

The "initVideo" function contains our NetStream and NetConnection functions as well as the variable we want to use for the actual video ID ("theVideo"). We set this variable to be the result of sending our video ID to our "getFLV.php" page on our server, and tell our Net Stream object to play the result.

nsVideo.play("http://www.ericmedine.com/temps/youtube/getFLV.php?id=" + theVideo);

Get it? Instead of copy and pasting a bunch of ID's into PHP pages, we've run our YouTube URL thru the PHP scripts automatically, and sent the result to our video player. If we want to make this some kind of standalone application, we would make a text box that we could maybe paste a URL into, and then have it call the parse URL function like so:
parseURL(http://www.ericmedine.com/temps/youtube/
convertURL.php?url=" + yourTextBoxValue);

Keep in Mind: This may not work for online use (for instance, if you re-purpose to embed in a web page) without including some cross domain XML scripts. That is beyond the scope of this tutorial (altho if I get around to building it, I'll post it here).

You also might want to have the video loop, or play/stop/pause, or even set trim points! That would be a tutorial for another day, however.