How to parse <media:content> in RSS using AS3? - actionscript-3

Am trying to parse the RSS feed from www.ted.com/talks/rss, I can access all normal tags using E4X but I have no idea how one parses the tags!
For example the
This is my code and I can traverse easily but I want to pull the media:content tags.
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
var _loader:URLLoader = new URLLoader();
var _request:URLRequest = new URLRequest("http://www.ted.com/talks/rss");
_loader.addEventListener(ProgressEvent.PROGRESS, onProgress);
_loader.addEventListener(IOErrorEvent.IO_ERROR, IOErrorHandler);
_loader.addEventListener(Event.COMPLETE, onLoadComplete);
_loader.load(_request);
}
private function onLoadComplete(e:Event):void
{
var _rssXML:XMLList = new XMLList(e.target.data);
for (var i:int = 0; i < 50; i++ )
{
//trace(_rssXML.channel.item.link[i]);
trace(_rssXML.channel.item[i]);
}
}
Any help would be appreciated.

Sounds like your using MRSS specifications. You want to look into QName to access qualifying namespaces
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/QName.html

Related

Details about as3 Sound.extract() format?

Using the sound object .extract() method, it's possible to retrieve the uncompressed sound data as a ByteArray. What is the format of the extracted sound data? Custom internal format, or established format? Endian type?
I would like to be able to load sounddata directly into a byteArray and feed sound playback from this data (bypassing step 1 below).
Here are my experiments so far:
Step 1. Here I load a sound, extract the data content and save it to disc:
private var sound:Sound;
private var byteArray:ByteArray;
// loading a sound...
private function init():void {
sound = new Sound();
sound.addEventListener(Event.COMPLETE, onComplete);
sound.load(new URLRequest('short.mp3'));
}
// saving it to disc...
private function onComplete(e:Event):void {
byteArray = new ByteArray();
sound.extract(byteArray, 100000000);
byteArray.position = 0;
var file:FileReference = new FileReference();
file.save(byteArray, 'short.data');
}
Step 2. Here I load the data from disc and feed the playback of a new sound object with this data:
private var sound:Sound;
private var byteArray:ByteArray;
// loading a sound...
private function init():void {
file = new FileReference();
file.addEventListener(Event.COMPLETE, onLoaded);
file.addEventListener(Event.SELECT, onSelected);
file.browse();
}
private function onSelected(e:Event):void {
file.load();
}
private function onLoaded(e:Event):void {
trace('loaded');
trace(file.data.length);
byteArray = file.data;
sound2 = new Sound();
sound2.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData);
sound2.play();
}
private function onSampleData(e:SampleDataEvent):void {
trace(e.position);
for (var i:int = 0; i < 2048; i++) {
var left:Number = byteArray.readFloat();
var right:Number = byteArray.readFloat();
e.data.writeFloat(left * 0.2);
e.data.writeFloat(right * 0.2);
}
}
I would like to be able of accomplishing this second step (loading data from disc and feeding a sound object playback) by using an external converter utility (sox or something).
/ Jonas
It should be standard 32bit floats, one per channel per sample. So I would try -e floating-point for sox, I haven't tried though but I would do it with a certain level of confidence... :) Floats are endian-independent, I believe...

URLLoader loads multi files and the result order is the same as call load()

Since URLLoader is async, how to make sure the order of data from server side is the same as the loader.load() call? In other words, the data order in totalResults is the same order of url-related content?
Following is code snippet:
1.for each(var url in urls) {
loadData(url);
}
2.private function loadData(url:String):void {
var urlLoader:URLLoader = new URLLoader();
urlLoader.addEventListener(Event.COMPLETE, completeHandler);
var request:URLRequest = new URLRequest(url);
urlLoader.load(request);
}
3.private function completeHandler(event:Event):void {
var loader:URLLoader = URLLoader(event.target);
var result:Object = loader.data;
totalResults.push(result);// suppose totalResults is Array and a property in the class.
}
You can extend URLLoader class functionality.
dynamic class DynamicURLLoader extends URLLoader { }
Then store data ( in your case probably index ) in loader object, before requesting:
var urlLoader:DynamicURLLoader = new DynamicURLLoader();
urlLoader.index = ...
After response, retrieve that data ( in your case index ):
var loader:DynamicURLLoader = DynamicURLLoader(event.target);
totalResults[ loader.index ] = loader.data;
you could either use BulkLoader - it got this build-in. or you have to build your own queue, where one file is loaded after the other.
code snippet:
0.var queue:Array, totalResults:Array = [];
var urlLoader:URLLoader = new URLLoader();
urlLoader.addEventListener(Event.COMPLETE, completeHandler);
1.for each(var url in urls) {
loadData(url);
}
2.private function loadData(url:String):void {
var request:URLRequest = new URLRequest(url);
queue.push(request);
}
3.private function doQueue() {
if (queue.length > 0) {
var arr:Array = queue.splice(0,1);
var req:URLRequest = arr[0] as URLRequest;
urlLoader.load(req);
}
else {
// queue done !
trace(totalResults);
}
}
4.private function completeHandler(event:Event):void {
var loader:URLLoader = URLLoader(event.target);
var result:Object = loader.data;
totalResults.push(result);// suppose totalResults is Array and a property in the class.
doQueue();
}
You can load each url sequentially one after the other in order like in the following example:
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.net.URLLoader;
import flash.net.URLRequest;
public class Main extends Sprite
{
private var _urls:Vector.<String>;
private var _counter:int;
private var _data:Array;
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}// end function
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
_urls = Vector.<String>(["text1.txt", "text2.txt", "text3.txt" ]);
_counter = 0;
_data = [];
loadNext();
}// end function
private function loadNext():void
{
var urlLoader:URLLoader = new URLLoader();
urlLoader.addEventListener(Event.COMPLETE, onComplete);
urlLoader.load(new URLRequest(_urls[_counter]));
}// end function
private function onComplete(event:Event):void
{
_data.push((event.target as URLLoader).data);
if (_counter++ == (_urls.length - 1)) trace("complete");
else loadNext();
}// end function
}// end class
}// end package
The methods loadNext() and onComplete() act as a loop. When loadNext() is called, an URLLoader object is instantiated and loads an url in the Vector.<String> object _urls. It uses the _counter object as a counter that is increment upon each UrlLoader object's "complete" event.
When the onComplete() event handler is called, it pushes the data loaded by the URLLoader object into an array called _data. Finally an if statement checks to see if all the urls have been loaded, if not, it increments the counter, if so, it executes the rest of the code in the application, in this case trace("complete");.
Why not just use HTTPService, which allows you to use AsyncToken to determine which initial call resulted in which initial result? That way, you don't have to wait between calls (with the performance degradation that implies.) Here is some documentation about how it works with Remoting, which is similar http://flexdiary.blogspot.com/2008/11/more-thoughts-on-remoting.html . With HttpService, you get back the token as a result of the send() method.

Problem with progressEvent Listener

for (i=0; i < _xmlContents.img_array.length; i++)
{
_loader = new Loader();
_loader.name = "image"+i;
_loader.load(new URLRequest(_xmlContents.img_array[i]));
_loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onLoadingAction);
//Event.COMPLETE listnere
//error Handler
}
private function onLoadingAction(e:ProgressEvent):void
{
_preLoader = new Preloader();
//addChild(_preLoader);
trace(_loader.name);
}
I want to add preloader for every image in the xml. Now I am getting for last image only.
(consider if xml length is 5, it will trace image4 only)
How can I add that?
It is because you have one _loader object. In every loop step you overwrite this loader with new one so previous image stops loading. You should use new loaders for every image:
for (i=0; i < _xmlContents.img_array.length; i++)
{
// create new loader instance, not use a global one
var _loader:Loader = new Loader();
_loader.name = "image"+i;
_loader.load(new URLRequest(_xmlContents.img_array[i]));
_loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onLoadingAction);
//Event.COMPLETE listnere
//error Handler
_preLoader = new Preloader();
//addChild(_preLoader);
}
private function onLoadingAction(e:ProgressEvent):void
{
// trace(e.bytesLoaded, e.bytesTotal);
}
First of all your event listeners attached to the loader instances before the last one (e.g. loaders 0 to 3) are there. They will still be there for a long long time. Remove them!
ActionScript is a very nice language - use it's power :)
for (i=0; i < img_array.length; i++)
{
_loader = new Loader();
_loader.name = "image"+i;
_loader.load(new URLRequest(img_array[i]));
_loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS,
function onLoadingAction(e:ProgressEvent):void
{
trace((e.target as LoaderInfo).loader.name);
//Do whatever you want to do here like removing event listener
if ((e.target as LoaderInfo).bytesLoaded == (e.target as LoaderInfo).bytesTotal)
{
(e.target as LoaderInfo).removeEventListener(ProgressEvent.PROGRESS, onLoadingAction);
trace("Event listener for " + (e.target as LoaderInfo).loader.name + " removed ");
}
}, false, i * 1000 /* you can use priority if want to maintain some order in event handling*/);
}
ActionScript provides you with the ability to name inline functions and to have a reference to them. Use this approach when you don't need to keep a reference to some object.
Good luck and have fun!
for (i=0; i < _xmlContents.img_array.length; i++)
{
_loader = new Loader();
_loader.name = "image"+i;
_loader.load(new URLRequest(_xmlContents.img_array[i]));
_loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS,
onLoadingAction);
//Event.COMPLETE listnere
//error Handler
_preLoader = new Preloader();
_bgBox.addChild(_preLoader);
}
This way is solves my problem.. But I don't think so, it's a good way.
I dont think the array is necessary and I think you'll be on a better track adding 4 instances of the preloader by calling it from Event.INIT as opposed to repeatedly adding it by using ProgressEvent.PROGRESS.
You your _loader object is defined inside the function and not referenced anywhere else, so it is garbage collected after the function ends. Create an array of loaders and push the loaders to them each time.
private var _loadersArray:Array=[]; //outside the function
for (i=0; i < _xmlContents.img_array.length; i++)
{
// create new loader instance, not use a global one
var _loader:Loader = new Loader();
_loader.name = "image"+i;
_loader.load(new URLRequest(_xmlContents.img_array[i]));
_loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onLoadingAction);
_loadersArray.push(_loader);
//Event.COMPLETE listnere
//error Handler
}
private function onLoadingAction(e:ProgressEvent):void
{
_preLoader = new Preloader();
//addChild(_preLoader);
// get current loader instance
var _loader:Loader = e.target.loader;
trace(_loader.name);
}

How can I use addEventListener and return something that's changed from it in Actionscript?

private function getTitle(src:String):String{
var urlLoader:URLLoader = new URLLoader();
var rssURLRequest:URLRequest = new URLRequest(src);
var rss:XML = new XML;
var t:String = src;
urlLoader.addEventListener(Event.COMPLETE,
function(event:Event):void{
rss = XML(urlLoader.data);
t = rss.channel.title.toString();
});
return t;
}
I'm aware that this code doesn't work because the anonymous function doesn't work until after t is returned. How would I make it so that it works?
You won't be able to return the loaded data from this method. The reason for this is because the loading is asynchronous and doesn't not block the execution of subsequent code. Your best option is to move the vars out of the scope of the function and to write a second function to handle the COMPLETE event.
Something like the following should work:
var rss:XML;
var t:String;
var path:String = "some path";
var urlLoader:URLLoader = new URLLoader();
private function getTitle(src:String):String
{
urlLoader.load( new URLRequest( src ) );
urlLoader.addEventListener(Event.COMPLETE, onComplete );
}
private function onComplete(event:Event):void
{
rss = XML(urlLoader.data);
t = path + rss.channel.title.toString();
}
I realize that this doesn't really answer the question directly, though it is the best practice for handling data loading. If you really want to stop any code from executing before the data is loaded, it may be possible use a while loop after the addEventListener line to halt the player from until the data is loaded. This should probably be considered a not so elegant hack though.
private function getTitle(src:String):String
{
var urlLoader:URLLoader = new URLLoader();
var rssURLRequest:URLRequest = new URLRequest(src);
var rss:XML = new XML;
var t:String = src;
var complete:Boolean;
urlLoader.addEventListener(Event.COMPLETE,
function(event:Event):void
{
rss = XML(urlLoader.data);
t = rss.channel.title.toString();
complete = true;
});
while( !complete ) { /* sleep hack */ }
return t;
}
I haven't tested this, but it seems like it could work. The first example is recommended.

Passing Informaton for Files with Loader

I'm using Loader to get the image data out of a ByteArray. The problem is that I need to store that image data with a name (which is known beforehand) once it's passed to the complete handler. Since the operation is asynchronous, I can't be sure which image of multiple will finish loading first, so it seems I need to pass the information with it somehow... I can't seem to find any properties of Loader that pass any vaguely useful information along.
Any recommendations on how I might accomplish this?
Couldn't you just use the Loader.name property?
var ldr:Loader = new Loader();
ldr.name = 'name_of_the_loader';
ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderListener);
ldr.loadBytes(aByteArray);
...
function loaderListener(event:Event):void {
trace('name of the completed loader is '+LoaderInfo(event.target).loader.name);
}
Could you provide some code?
private var loaders:Array = [];
private var names:Array = [];
//inside loadImages method
for(i = 0; i < len; i++)
{
var ldr:Loader = new Loader();
//add listeners and call load
loaders.push(ldr)
names.push(name-of-ith-image);
}
private function onLoadComplete(e:Event):void
{
var index:Number = loaders.indexOf(LoaderInfo(e.target).loader);
var requiredName:String = names[index];
trace(requiredName);
}
First solution would be to use a Dictionary to map the Loader instances to names.
Like this:
private var names : Dictionary = new Dictionary();
...
var ldr : Loader = new Loader();
names [ ldr ] = 'someName';
ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderListener);
ldr.loadBytes(aByteArray);
...
function loaderListener(event:Event):void {
trace('name of the completed loader is '+ names[ event.target ] );
}
The other solution would be to use a functor, like this:
var ldr : Loader = new Loader();
ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, createListener( 'someName' ) );
ldr.loadBytes(aByteArray);
...
function createListener( imgName : String ) : Function {
return function ( event : Event ) : void {
trace('name of the completed loader is '+ imgName );
}
}
loader.contentLoaderInfo.url will be having URL of the loaded image (e.g http://sometURL/image1.jpg).