Archive for April, 2009
AS3 Google Weather API lib – Updated
Posted by Dennis in Actionscript 3, Util on April 28th, 2009
A while ago, I needed to integrate a weather forecast into an actionscript application I was developing.
After some initial searching, I quickly stumbled upon the yahoo weather API.
I dug into this some more and even found an AS3 library for getting the weather forecast! But then, the dissapointment came. When implementing this, you could only get a basic forecast for the current date, with a link to go to the full weather forecast. As demonstrated here.
After some more searching for the right API to suit our needs (a very basic one), I found the google weather API.
The use of the API is as follows:
1) you send a http request like the following : http://www.google.com/ig/api?weather=Paris,France&hl=fr to the API
2) you receive an XML response containing weather forecast data for the next 4 days for a given location
So actually you could say it is more like a feed than an API.
But this seemed to be exactly what I needed to get the job done.
The main problem I ran into was that the API was not officially documented, so I had to do some scraping (monitoring requests and responses with charles) to find out how it worked. And of course putting the pieces together reading several blogs / forums etc.
More unofficial documentation can be found here
Also, I never found a proper actionscript library that enables us to retreive this data. That’s why I decided to stop wasting precious time and just make it myself.
Basically all I did was create a wrapper library that does the request, gets the response and handles errors in an easy to use manner.
This resulted in a fully functional AS3 library for use in flash or flex to retreive weather forecast data from google weather.
Note: When loading the xml and images from an external source, make sure to use a proxy in order to avoid flash player security issues.
In some cases, you also need to put a crossdomain.xml on your server in order to enable your swf to acces external content.
I tried to put comments in the example as much as I could to make everything as clear as possible.
Feel free to use this library in your projects / play around with it.
If you have any remarks, additions, request, questions… just post them here.
This is the basic result, just displaying the data received from the api. Nothing less, nothing more:
UPDATE
Google made changes to the API, causing the standard images not to show anymore.
A minor fix in the GoogleWeatherService.as class solved the issue.
Changed
1 | public static const BASE_IMAGE_URL:String = "http://www.google.com/ig"; |
to
1 | public static const BASE_IMAGE_URL:String = "http://www.google.com"; |
View the new example here
Or download the SWC and source files with the above usage example here: AS3 Google Weather lib (1266)
Youtube video in AS3 FLVPlayback
Posted by Dennis in Actionscript 3 on April 23rd, 2009
Lately,
I’ve been busy trying to get youtube movies to play in the standard FLVPlayback component.
Getting this to work was really a pain in the butt now and then, especially because while I was working on this,
youtube changed some stuff on their side undoing all my previous work. But hey, stuff happens!
In the end perseverance and a lot of patience finally resulted in a working solution.
I will break up the solution into several pieces, describing the steps that need to be taken in order for this to work.
1) We need to get the video (.flv) from the youtube server.
To achieve this, one must of course understand how to get this video.
To start off, just navigate to a youtube movie. e.g. http://www.youtube.com/watch?v=vE_WqdKbTvY , then right-click anywhere in page and watch the page source code.
In the source code, search for the string “swfArgs”, which will look something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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-z4CoWJgWn2Pfu77I=", "hl": "en", "plid": "AARoEenKN8A9FYLn", "vq": null, "ad_module": "http://s.ytimg.com/yt/swf/ad-vfl91517.swf", "cr": "NL", "tk": "j7SixOLxSJ1xxBIwWubnN9sXiu7hrejTmRv4ruMx4N3OaeyhN0xImQ=="}; |
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 / download the video.
This token expires after a given period of time, so I can’t place a permanent download link for the flv file here.
So now that we have the parameters of the video we need to fill them in as follows into a fixed url, used by youtube to retreive the flv video:
http://www.youtube.com/get_video.php?video_id=[id]&t=[t]
In our case:
http://www.youtube.com/get_video.php?video_id=vE_WqdKbTvY&t=vjVQa1PpcFO6TnxVA4_nkqbqKN-z4CoWJgWn2Pfu77I=
When navigating to this url with your browser, the video will start downloading
Don’t bother trying to open this last link, because as stated before, the token will probably have expired by the time you read this.
(Also the token is linked to the IP that first requested it, which in this case would be mine. More about this later)
If you would like to test if and how this works, just follow all the steps described above and see for yourself.
Pretty cool huh?
Now that we know how to get the .flv video of any youtube movie, we need to find a way to automatically do this, without the need to go through all the steps again.
We will use a php proxy script that eliminates all sandbox issues, accepts any youtube url and then returns the physical video (.flv) as if it were present on our own server.
However, this script will be a mere redirect to the location of the video on youtube servers, the file will not be physically downloaded by your server.
To create a script like this, I did some searching and quickly stumbled upon a script I found here.
This worked fine at first, but as I was saying youtube changed some things server side along the way.
That’s why i needed to search for a solution and needed to modify the script in order to getting it to work again.
The problem was caused because youtube changed the token idea.
A token is now linked to an IP address, meaning that the video can only be downloaded by the IP address that first requested the token.
This is a problem when trying to do this in-browser, where the browser is on the localhost, and the script is on a server and thus causing an IP mismatch. The result is that the file cannot be accessed.
After alot of searching around the final solution is to retreive the headers from the youtube download url and extracting the location of the flv that is embedded.
The result for the php proxy script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | <?php $id = trim($_REQUEST['id']); $url = "http://www.youtube.com/watch?v=" . $id; $url = $url . "&fmt=18"; //Gets the movie in High Quality, uncomment this line to get it in normal 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 ?> |
An example of correct usage:
Navigate to http://www.dennisjaamann.com/demo/youtubeFLVPlayback/php/getYoutubeFLV.php?id=[videoID],
where the videoID is the id from the youtube movie http://www.youtube.com/watch?v=vE_WqdKbTvY
In this case: http://www.dennisjaamann.com/demo/youtubeFLVPlayback/php/getYoutubeFLV.php?id=vE_WqdKbTvY
When navigating to the url above, you will notice that the script will redirect you to the physical location of the .flv file.This will start the download prompt.
Isn’t this exactly what we need?
2) Workaround for FLVPlayback
When we take the url retreived above and try to play it in the standard FLVPlayback component as follows:
1 2 3 4 | var videoPlayer:FLVPlayback = new FLVPlayback(); addChild(videoPlayer); videoPlayer.source = "http://www.dennisjaamann.com/demo/youtubeFLVPlayback/php/getYoutubeFLV.php?id=vE_WqdKbTvY"; videoPlayer.play(); |
The video doesn’t play and we get the following error:
VideoError: 1005: Invalid xml: URL: “http://www.dennisjaamann.com/demo/youtubeFLVPlayback/php/getYoutubeFLV.php?id=vE_WqdKbTvY&FLVPlaybackVersion=2.1″ No root node found; if url is for an flv it must have .flv extension and take no parameters
at fl.video::SMILManager/http://www.adobe.com/2007/flash/flvplayback/internal::xmlLoadEventHandler()
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at flash.net::URLLoader/onComplete()
Which is kind of a suprise, because when we open the source url in our browser there is clearly an .flv file there.
However, when analyzing the error message we can clearly see the problem. The key part here is:
“if url is for an flv it must have .flv extension and take no parameters”
When taking a look at our url http://www.dennisjaamann.com/demo/youtubeFLVPlayback/php/getYoutubeFLV.php?id=vE_WqdKbTvY,
it is obvious that it doesn’t end on .flv and that our url accepts parameters.
This behaviour is caused by a few lines of code in the NCManager class used by the FLVPlayback component.
There, the check is done whether the url ends on .flv and that the url does not accept parameters.
This leaves us 2 options:
- Extend the NCManager, overwrite this behaviour and make the FLVPlayback use this custom class
- Find a way to give the NCManager a correctly formatted url, so that it can stop nagging
I prefer the second option because it leaves the NCManager unmodified and hereby we can eliminate any unwanted behaviour.
Also, since I have a little php experience, I quickly realized that this had the funky smell of url rewriting.
With url rewriting you can take any url and format it to the format required which is exactly what we need here…
So we need to change the original url
http://www.dennisjaamann.com/demo/youtubeFLVPlayback/php/getYoutubeFLV.php?id=vE_WqdKbTvY
to
http://www.dennisjaamann.com/demo/youtubeFLVPlayback/videos/vE_WqdKbTvY.flv
This can be achieved by adding a .htaccess file to your project folder on the web server.
The code below is also included in the source files at the bottom of this post. But please note, not all operating systems show a .htaccess file.
This is because that it sometimes is a hidden file type.
Here’s the code for that:
1 2 3 | Options +FollowSymlinks RewriteEngine on RewriteRule ^videos/([^/]+).flv php/getYoutubeFLV.php?id=$1 [NC] |
And that’s all folks!
When we now execute our code again with the modified url, the NCManager has stopped nagging and the videoplayer plays the wanted video.
1 2 3 4 | var videoPlayer:FLVPlayback = new FLVPlayback(); addChild(videoPlayer); videoPlayer.source = "http://www.dennisjaamann.com/demo/youtubeFLVPlayback/videos/vE_WqdKbTvY.flv"; videoPlayer.play(); |
This workaround took like a minute or so to be succesful, leaves the the NCManager unchanged and does the trick.
That’s why I’ve chosen this solution because it is simple and elegant.
Read more about basic url rewriting here
3) Create the player.
All that is left now is to create our application. I have prepared an example in flex (sdk 3.3)
This is the result:
View the full example with source here
Or download the example source files here: youtubeFLVPlayback (1512)
