I wrote a tiny program for getting Youtube video information by hitting Youtube API in ActionScript 3.0.
Some generic classes or methods might be missing but I think you can easily guess what they are doing and can add them easily yourself.
You can downsize this class if you need. I think a bit too much for this youtube example :P
Some generic classes or methods might be missing but I think you can easily guess what they are doing and can add them easily yourself.
Example Usage
Ok, starting from the example usage.package utils.video { import flash.display.Sprite; import utils.file.SyncFileSaveDownLoader; import utils.ITaskProgressCounter; import utils.TaskProgressCounter; import utils.video.youtube.YoutubeAPI; import utils.video.YoutubeFLVURLGetEvent; import utils.video.youtube.YoutubeLocalCacheManager; import utils.video.youtube.YoutubeVideoEntryDispatcher; import utils.video.youtube.YoutubeVideoEntryEvent; public class Tester extends Sprite { private var downloader:SyncFileSaveDownLoader = new SyncFileSaveDownLoader(); private var youtubeLocalCacheManager:YoutubeLocalCacheManager = new YoutubeLocalCacheManager("c:\\temp\\youtube", downloader); public function Tester() { var dispatcher:YoutubeVideoEntryDispatcher = new YoutubeVideoEntryDispatcher(); dispatcher.addEventListener(YoutubeVideoEntryEvent.CREATED, created); var api:YoutubeAPI = new YoutubeAPI("utf-8", dispatcher); api.searchByKeywords(10, "Google IO"); // directtly hitting api // api.exec("http://gdata.youtube.com/feeds/videos?start-index=1&max-results=25", 2); // api.exec("http://gdata.youtube.com/feeds/users/youtube/uploads?start-index=1&max-results=25", 2); } public function created(evt:YoutubeVideoEntryEvent):void { // debug print trace("---"); trace(evt.videoEntry.author.name); trace(evt.videoEntry.playerURL); // download thumbnails youtubeLocalCacheManager.downloadThumbnails(evt.videoEntry); } } }
Main Classes
package utils.video.youtube { import flash.net.URLLoader; import flash.net.URLLoaderDataFormat; import flash.events.Event; import flash.events.TimerEvent; import flash.net.URLRequest; import flash.events.ProgressEvent; import flash.utils.ByteArray; import utils.StringUtils; import utils.tool.ByteLoadHelper; import utils.tool.ByteLoadEvent; public class YoutubeAPI { private var charEncode:String; private var videoParser:YoutubeVideoEntryParser; private var baseURL:String = "http://gdata.youtube.com/feeds/videos/"; public static const NS_ATOM:Namespace = new Namespace("", "http://www.w3.org/2005/Atom"); public static const NS_MEDIA:Namespace = new Namespace("media","http://search.yahoo.com/mrss/"); public static const NS_GD:Namespace = new Namespace("gd","http://schemas.google.com/g/2005"); public static const NS_YT:Namespace = new Namespace("yt","http://gdata.youtube.com/schemas/2007"); public static const Q_ENTRY:QName = new QName(NS_ATOM, "entry"); public static const Q_LINK:QName = new QName(NS_ATOM, "link"); public function YoutubeAPI(charEncode:String, videoParser:YoutubeVideoEntryParser) { this.charEncode = charEncode; this.videoParser = videoParser; } public function searchByKeywords(max:int, ...args):void { var queryStr:String = StringUtils.concatAllAsString("+", args); if(queryStr.length > 0){ exec(baseURL + "?vq=" + queryStr, max); } } public final function exec(url:String, max:int):void { var loader:ByteLoadHelper = new ByteLoadHelper(); loader.addEventListener(ByteLoadEvent.COMPLETE, completeHandler); loader.load(url); var maxInit:uint = max; function completeHandler(evt:ByteLoadEvent):void { loader.removeEventListener(ByteLoadEvent.COMPLETE, completeHandler); var bytes:ByteArray = evt.data; var rss:String = bytes.readMultiByte(bytes.bytesAvailable, charEncode); var xml:XML = new XML(rss); for each(var entryXML:XML in xml.child(Q_ENTRY)) { if (max <= 0) { return; } try { videoParser.parse(entryXML); } catch (e:TypeError) { // should so proper error handling trace(e); } max--; } if(maxInit > 0){ var linkNext:XMLList = xml.child(Q_LINK).(attribute("rel") == "next"); for each(var elemXML:XML in linkNext) { exec(elemXML.attribute("href"), maxInit); } } } } } }
package utils.video.youtube { import flash.events.EventDispatcher; public class YoutubeVideoEntryDispatcher extends EventDispatcher implements YoutubeVideoEntryParser { private var factory:YoutubeVideoEntryFactory; public function YoutubeVideoEntryDispatcher() { this.factory = new YoutubeVideoEntryFactory(); } public function parse(entryXML:XML):void { dispatchEvent(new YoutubeVideoEntryEvent(factory.create(entryXML), YoutubeVideoEntryEvent.CREATED)); } } }
package utils.video.youtube { import utils.StringUtils; import utils.video.ThumbnailInfo; public class YoutubeVideoEntryFactory { public function YoutubeVideoEntryFactory() { } public function create(entryXML:XML):YoutubeVideoEntry { var entry:YoutubeVideoEntry = new YoutubeVideoEntry(); var authorList:XMLList = entryXML.child(new QName(YoutubeAPI.NS_ATOM, "author")); entry.author = new YoutubeAuthor( authorList.child(new QName(YoutubeAPI.NS_ATOM, "name")), authorList.child(new QName(YoutubeAPI.NS_ATOM, "uri")) ); var idList:XMLList = entryXML.child(new QName(YoutubeAPI.NS_ATOM, "id")); var idURL:String = idList.toString(); var id:String = StringUtils.extractLast(idURL, "/"); entry.id = id; var mediaGroupList:XMLList = entryXML.child(new QName(YoutubeAPI.NS_MEDIA, "group")); for each(var mediaGroup:XML in mediaGroupList) { for each(var elemXML:XML in mediaGroup.child(new QName(YoutubeAPI.NS_MEDIA, "player")).attribute("url")) { entry.playerURL = elemXML.toString(); } for each(elemXML in mediaGroup.child(new QName(YoutubeAPI.NS_MEDIA, "keywords"))) { var keywords:String = elemXML.toString(); var tags:Array = keywords.split(","); for each(var tag:String in tags ) { entry.tags.push(StringUtils.trim(tag)); } } for each(elemXML in mediaGroup.child(new QName(YoutubeAPI.NS_MEDIA, "title"))) { entry.title = elemXML.toString(); } var categoryList:XMLList = mediaGroup.child(new QName(YoutubeAPI.NS_MEDIA, "category")).(hasOwnProperty("@label")); for each(elemXML in categoryList) { entry.category = elemXML.toString(); } for each(elemXML in mediaGroup.child(new QName(YoutubeAPI.NS_MEDIA, "description"))) { entry.description = elemXML.toString(); } for each(elemXML in mediaGroup.child(new QName(YoutubeAPI.NS_MEDIA, "thumbnail"))) { entry.thumbnails.push(new ThumbnailInfo( id, elemXML.attribute("url").toString(), int(elemXML.attribute("width")), int(elemXML.attribute("height").toString()), decodeTimeStrToSec(elemXML.attribute("time").toString()) )); } } for each(elemXML in entryXML.child(new QName(YoutubeAPI.NS_GD, "rating"))) { entry.rating = new YoutubeRating( int(elemXML.attribute("min")), int(elemXML.attribute("max")), int(elemXML.attribute("numRaters")), Number(elemXML.attribute("average")) ); } for each(elemXML in entryXML.child(new QName(YoutubeAPI.NS_YT, "statistics"))) { entry.statistics = new YoutubeStatistics( int(elemXML.attribute("viewCount")), int(elemXML.attribute("favoriteCount")) ); } return entry; } public static function decodeTimeStrToSec(time:String):uint { var splitted:Array = time.split(":"); return uint(splitted[0]) * 3600 + uint(splitted[1]) * 60 + uint(splitted[2]); } } }
package utils.video.youtube { import flash.filesystem.File; import utils.file.IDownloader; import utils.Utils; import utils.video.IFlvUrlGetter; import utils.video.ThumbnailInfo; public class YoutubeLocalCacheManager { private var _downloader:IDownloader; private var _baseDir:String public function YoutubeLocalCacheManager(baseDir:String, downloader:IDownloader) { this._downloader = downloader; this._baseDir = baseDir; } protected function getDir(entry:YoutubeVideoEntry):File { var id:String = entry.id; var dir:File = new File(_baseDir + File.separator + id); if (!dir.exists) { dir.createDirectory(); } return dir; } public function downloadThumbnails(youtubeEntry:YoutubeVideoEntry):void { for each(var thumb:ThumbnailInfo in youtubeEntry.thumbnails) { var fileName:String = Utils.getFileName(thumb.url, "/"); var path:String = getDir(youtubeEntry).nativePath + File.separator + fileName; if(!new File(path).exists){ _downloader.download(thumb.url, path); } } } } }
Event Classes
package utils.video.youtube { import flash.events.Event; public class YoutubeVideoEntryEvent extends Event { public static const CREATED:String = "youutbe_video_entry_created"; private var _videoEntry:YoutubeVideoEntry; public function YoutubeVideoEntryEvent(videoEntry:YoutubeVideoEntry, type:String, bubbles:Boolean=false, cancelable:Boolean=false) { super(type, bubbles, cancelable); this._videoEntry = videoEntry; } public override function clone():Event { return new YoutubeVideoEntryEvent(_videoEntry, type, bubbles, cancelable); } public override function toString():String { return formatToString("YoutubeVideoEntryEvents", "type", "bubbles", "cancelable", "eventPhase"); } public function get videoEntry():YoutubeVideoEntry { return _videoEntry; } } }
Data Object Classes
Nothing special... :) simply flattened xml object to classes (we can able to use more common library to mapping between class and xml).package utils.video.youtube { import utils.TaggableEntry; import utils.video.ThumbnailInfo; public class YoutubeVideoEntry { private var _tags:Vector.<String> = new Vector.<String>(); private var _id:String; private var _playerURL:String; private var _thumbnails:Vector.<ThumbnailInfo>; private var _rating:YoutubeRating; private var _description:String; private var _statistics:YoutubeStatistics; private var _author:YoutubeAuthor; private var _category:String; private var _title:String; public function YoutubeVideoEntry() { this._thumbnails = new Vector.<ThumbnailInfo>(); } public function set id(id:String):void { this._id = id; } public function get id():String { return _id; } public function set playerURL(url:String):void { this._playerURL = url; } public function get playerURL():String { return _playerURL; } public function get thumbnails():Vector.<ThumbnailInfo> { return _thumbnails; } public function set rating(rate:YoutubeRating):void { this._rating = rate; } public function get rating():YoutubeRating { return _rating; } public function set description(description:String):void { this._description = description; } public function get description():String { return _description; } public function set statistics(statistics:YoutubeStatistics):void { this._statistics = statistics; } public function get statistics():YoutubeStatistics { return this._statistics; } public function set author(author:YoutubeAuthor):void { this._author = author; } public function get author():YoutubeAuthor { return this._author; } public function set category(category:String):void { this._category = category; } public function get category():String { return this._category; } public function set title(title:String):void { this._title = title; } public function get title():String { return this._title; } public function get key():String { return id; } public function get tags():Vector.<String> { return _tags; } } }
package utils.video.youtube { public class YoutubeAuthor { private var _name:String; private var _uri:String; public function YoutubeAuthor(name:String, uri:String) { this._name = name; this._uri = uri; } public function get name():String { return _name; } public function get uri():String { return _uri; } } }
package utils.video.youtube { public class YoutubeRating { private var _max:int, _min:int, _numRaters:int, _average:Number; public function YoutubeRating(max:int, min:int, numRaters:int, average:Number) { this._max = max; this._min = min; this._numRaters = numRaters; this._average = average; } public function get max():int { return _max; } public function get min():int { return _min; } public function get numRaters():int { return _numRaters; } public function get average():Number { return _average; } } }
package utils.video.youtube { public class YoutubeStatistics { private var _viewCount:int; private var _favoriteCount:int; public function YoutubeStatistics(viewCount:int, favoriteCount:int) { this._viewCount = viewCount; this._favoriteCount = favoriteCount; } public function get favoriteCount():int { return _favoriteCount; } public function get viewCount():int { return _viewCount; } } }
Helper Classes
Mainly class for downloading resources.You can downsize this class if you need. I think a bit too much for this youtube example :P
package utils.file { import flash.events.Event; import flash.events.EventDispatcher; import flash.events.IOErrorEvent; import flash.events.ProgressEvent; import flash.events.SecurityErrorEvent; import flash.net.URLLoader; import flash.net.URLLoaderDataFormat; import flash.net.URLRequest; import utils.IOUtils; public class SyncFileSaveDownLoader extends EventDispatcher implements IDownloader { private var id:int; public function SyncFileSaveDownLoader() { this.id = 0; } public function download(url:String, path:String, listenProgress:Boolean=false):FileDownloadTask { var loader:URLLoader = new URLLoader(); var task:FileDownloadTask = new FileDownloadTask(id.toString(), url, path, loader); loader.dataFormat = URLLoaderDataFormat.BINARY; loader.addEventListener(Event.COMPLETE, completeHandler); loader.addEventListener(IOErrorEvent.IO_ERROR, ioEllorHandler); if(listenProgress){ loader.addEventListener(ProgressEvent.PROGRESS, handleProgress); } loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler); loader.load(new URLRequest(url)); id++; return task; function handleProgress( pevt:ProgressEvent ):void { var total:Number = pevt.bytesTotal; if (total != 0) { task.totalBytes = total; task.percentage = Math.round( pevt.bytesLoaded / total * 100 ); task.status = FileDownloadTask.DOWNLOADING; } } function completeHandler(cevt:Event):void { saveByteData(cevt.target.data, path); task.status = FileDownloadTask.SUCCESS; finalize(); } function securityErrorHandler(e:SecurityErrorEvent):void { task.status = FileDownloadTask.FAIL; finalize(); } function ioEllorHandler(e:IOErrorEvent):void { task.status = FileDownloadTask.FAIL; finalize(); } function finalize():void { loader.removeEventListener(Event.COMPLETE, completeHandler); loader.removeEventListener(ProgressEvent.PROGRESS, handleProgress); loader.removeEventListener(IOErrorEvent.IO_ERROR, ioEllorHandler); loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler); task.dispose(); } } public static function saveByteData(data:ByteArray, path:String):void { try { var file:File = new File(path); var fs:FileStream = new FileStream(); fs.open(file, FileMode.WRITE); fs.writeBytes(data); fs.close(); } catch (err:IOError) { trace(err); } } } }
package utils.file { import flash.events.Event; public class FileDownloadEvent extends Event { public static const SAVE_CONPLETE:String = "SaveComplete"; private var _url:String; private var _path:String; public function FileDownloadEvent(url:String, path:String, type:String, bubbles:Boolean=false, cancelable:Boolean=false) { super(type, bubbles, cancelable); this._url = url; this._path = path; } public override function clone():Event { return new FileDownloadEvent(_url, _path, type, bubbles, cancelable); } public override function toString():String { return formatToString("FileDownloadEvent", "type", "bubbles", "cancelable", "eventPhase"); } public function get url():String { return _url; } public function get path():String { return _path; } } }
package utils.file { import flash.net.URLLoader; public class FileDownloadTask { public static const DOWNLOADING:String = "Downloading"; public static const SAVING:String = "Saving"; public static const SUCCESS:String = "Success"; public static const FAIL:String = "Fail"; private var _id:String; private var _url:String; private var _path:String; private var _status:String; private var _percentage:Number; private var _totalBytes:Number; private var _urlLoader:URLLoader; public function FileDownloadTask(id:String, url:String, path:String, urlLoader:URLLoader) { this._id = id; this._url = url; this._path = path; this._urlLoader = urlLoader; } public function get url():String { return _url; } public function get id():String { return _id; } public function get path():String { return _path; } public function set status(status:String):void { if (status != DOWNLOADING && status != SUCCESS && status != FAIL && status != SAVING) { throw new Error("Invalid Status"); } this._status = status; } public function get status():String { return _status; } public function cancel():void { if (_urlLoader != null) { _urlLoader.close(); _urlLoader = null; } } public function dispose():void { _urlLoader = null; } public function get percentage():Number { return _percentage; } public function set percentage(value:Number):void { _percentage = value; } public function get totalBytes():Number { return _totalBytes; } public function set totalBytes(value:Number):void { _totalBytes = value; } } }
コメント