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;
}
}
}
コメント