├── src └── com │ └── doitflash │ └── remote │ └── youtube │ ├── VideoQuality.as │ ├── VideoType.as │ ├── YouTubeLinkParserEvent.as │ └── YouTubeLinkParser.as └── README.md /src/com/doitflash/remote/youtube/VideoQuality.as: -------------------------------------------------------------------------------- 1 | package com.doitflash.remote.youtube 2 | { 3 | 4 | /** 5 | * 6 | * @author Hadi Tavakoli - 11/6/2014 7:01 PM 7 | */ 8 | public class VideoQuality 9 | { 10 | public static const SMALL:String = "small"; 11 | public static const MEDIUM:String = "medium"; 12 | public static const HD720:String = "hd720"; 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /src/com/doitflash/remote/youtube/VideoType.as: -------------------------------------------------------------------------------- 1 | package com.doitflash.remote.youtube 2 | { 3 | 4 | /** 5 | * 6 | * @author Hadi Tavakoli - 11/6/2014 7:01 PM 7 | */ 8 | public class VideoType 9 | { 10 | public static const VIDEO_WEBM:String = "video/webm"; 11 | public static const VIDEO_MP4:String = "video/mp4"; 12 | public static const VIDEO_FLV:String = "video/x-flv"; 13 | public static const VIDEO_3GP:String = "video/3gpp"; 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /src/com/doitflash/remote/youtube/YouTubeLinkParserEvent.as: -------------------------------------------------------------------------------- 1 | package com.doitflash.remote.youtube 2 | { 3 | import flash.events.Event; 4 | 5 | /** 6 | * 7 | * @author Hadi Tavakoli - 11/6/2014 6:14 PM 8 | */ 9 | public class YouTubeLinkParserEvent extends Event 10 | { 11 | public static const COMPLETE:String = "onComplete"; 12 | public static const ERROR:String = "onError"; 13 | 14 | public static const VIDEO_HEADER_RECEIVED:String = "onVideoHeaderReceived"; 15 | public static const VIDEO_HEADER_ERROR:String = "onVideoHeaderError"; 16 | 17 | 18 | 19 | private var _param:*; 20 | 21 | public function YouTubeLinkParserEvent(type:String, data:*=null, bubbles:Boolean=false, cancelable:Boolean=false):void 22 | { 23 | _param = data; 24 | super(type, bubbles, cancelable); 25 | } 26 | 27 | /** 28 | * @private 29 | */ 30 | public function get param():* 31 | { 32 | return _param; 33 | } 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## AS3-youtube-parser-video-link ## 2 | This AS3 library can parse standard youtube links like **https://www.youtube.com/watch?v=QowwaefoCec** 3 | and will extract different elements of that video like available direct video addresses, video title 4 | and video thumbnail. It works with public unrestricted video files only. 5 | 6 | I must add that I wrote this library after being inspired by https://github.com/jeckman/YouTube-Downloader 7 | 8 | It works on Android/iOS/Windows/Mac AIR projects. 9 | 10 | USAGE: 11 | 12 | ```actionscript 13 | 14 | import com.doitflash.remote.youtube.YouTubeLinkParser; 15 | import com.doitflash.remote.youtube.YouTubeLinkParserEvent; 16 | import com.doitflash.remote.youtube.VideoType; 17 | import com.doitflash.remote.youtube.VideoQuality; 18 | 19 | var _ytParser:YouTubeLinkParser = new YouTubeLinkParser(); 20 | _ytParser.addEventListener(YouTubeLinkParserEvent.COMPLETE, onComplete); 21 | _ytParser.addEventListener(YouTubeLinkParserEvent.ERROR, onError); 22 | _ytParser.parse("https://www.youtube.com/watch?v=QowwaefoCec"); 23 | 24 | function onError(e:YouTubeLinkParserEvent):void 25 | { 26 | // removing listeners just for clean cosing reasons! 27 | _ytParser.removeEventListener(YouTubeLinkParserEvent.COMPLETE, onComplete); 28 | _ytParser.removeEventListener(YouTubeLinkParserEvent.ERROR, onError); 29 | trace("Error: " + e.param.msg); 30 | } 31 | 32 | function onComplete(e:YouTubeLinkParserEvent):void 33 | { 34 | // removing listeners just for clean coding reasons! 35 | _ytParser.removeEventListener(YouTubeLinkParserEvent.COMPLETE, onComplete); 36 | _ytParser.removeEventListener(YouTubeLinkParserEvent.ERROR, onError); 37 | 38 | trace("youTube parse completed..."); 39 | trace("video thumb: " + _ytParser.thumb); 40 | trace("video title: " + _ytParser.title); 41 | trace("possible found videos: " + _ytParser.videoFormats.length); 42 | 43 | trace("you can only access youtube public videos... no age restriction for example!"); 44 | trace("some video formats may be null so you should check their availablily..."); 45 | trace("to make your job easier, I built another method called getHeaders() which will load video headers for you! you can know the video size using these header information :) "); 46 | 47 | // let's find the VideoType.VIDEO_MP4 video format in VideoQuality.MEDIUM for this video 48 | // NOTICE: you should find your own way of selecting a video format! as different videos may not have all formats or qualities available! 49 | 50 | var chosenVideo:String; 51 | for (var i:int = 0; i < _ytParser.videoFormats.length; i++) 52 | { 53 | var currVideoData:Object = _ytParser.videoFormats[i]; 54 | if (currVideoData.mimeType.indexOf(VideoType.VIDEO_MP4) > -1 && currVideoData.quality == VideoQuality.MEDIUM) 55 | { 56 | chosenVideo = currVideoData.url; 57 | break; 58 | } 59 | } 60 | 61 | _ytParser.addEventListener(YouTubeLinkParserEvent.VIDEO_HEADER_RECEIVED, onHeadersReceived); 62 | _ytParser.addEventListener(YouTubeLinkParserEvent.VIDEO_HEADER_ERROR, onHeadersError); 63 | _ytParser.getHeaders(chosenVideo); 64 | } 65 | 66 | function onHeadersError(e:YouTubeLinkParserEvent):void 67 | { 68 | _ytParser.removeEventListener(YouTubeLinkParserEvent.VIDEO_HEADER_RECEIVED, onHeadersReceived); 69 | _ytParser.removeEventListener(YouTubeLinkParserEvent.VIDEO_HEADER_ERROR, onHeadersError); 70 | 71 | trace("Error: " + e.param.msg) 72 | } 73 | 74 | function onHeadersReceived(event:YouTubeLinkParserEvent):void 75 | { 76 | _ytParser.removeEventListener(YouTubeLinkParserEvent.VIDEO_HEADER_RECEIVED, onHeadersReceived); 77 | _ytParser.removeEventListener(YouTubeLinkParserEvent.VIDEO_HEADER_ERROR, onHeadersError); 78 | 79 | var lng:int = event.param.headers.length; 80 | var i:int; 81 | var currHeader:*; 82 | 83 | for (i = 0; i < lng; i++ ) 84 | { 85 | currHeader = event.param.headers[i]; 86 | trace(currHeader.name + " = " + currHeader.value); 87 | } 88 | 89 | // ok, we are happy! now let's download this video, like any other file you would download: 90 | download(event.param.url); 91 | } 92 | ``` -------------------------------------------------------------------------------- /src/com/doitflash/remote/youtube/YouTubeLinkParser.as: -------------------------------------------------------------------------------- 1 | package com.doitflash.remote.youtube 2 | { 3 | import flash.events.Event; 4 | import flash.events.IOErrorEvent; 5 | import flash.events.EventDispatcher; 6 | import flash.net.URLLoaderDataFormat; 7 | import flash.net.URLRequestMethod; 8 | import flash.net.URLLoader; 9 | import flash.net.URLVariables; 10 | import flash.net.URLRequest; 11 | import flash.net.URLRequestHeader; 12 | import air.net.URLMonitor; 13 | import flash.events.StatusEvent; 14 | import flash.events.HTTPStatusEvent; 15 | import flash.events.ProgressEvent; 16 | 17 | /** 18 | * Using this class will help you parse standard YouTube urls to find out different availble video formats and qualities. 19 | * if you want to simply support YouTube play on your apps, I suggest you to use the official YouTube API availble at: 20 | * https://developers.google.com/youtube/ 21 | * 22 | *

in rare cases like needing to play videos over texture when building Augmented Reality apps, you would need to 23 | * download the video file first and that's when this class will be helpful

24 | * 25 | *

This library is mainly based on the following PHP lib 26 | * https://github.com/jeckman/YouTube-Downloader

27 | * 28 | *

NOTICE: I cannot give any gurantee that this class would always work! because YouTube changes all the time 29 | * and the way to parse the information may change from time to time! This is an open source project, feel free to change it 30 | * any way you like to make it work again! :D All I can say is that it is working as today that I have built it! Nov 10, 2014

31 | * 32 | * @example use the lib like this: 33 | * 34 | * import com.doitflash.remote.youtube.YouTubeLinkParser; 35 | * import com.doitflash.remote.youtube.YouTubeLinkParserEvent; 36 | * import com.doitflash.remote.youtube.VideoType; 37 | * import com.doitflash.remote.youtube.VideoQuality; 38 | * 39 | * var _ytParser:YouTubeLinkParser = new YouTubeLinkParser(); 40 | * _ytParser.addEventListener(YouTubeLinkParserEvent.COMPLETE, onComplete); 41 | * _ytParser.addEventListener(YouTubeLinkParserEvent.ERROR, onError); 42 | * _ytParser.parse("https://www.youtube.com/watch?v=QowwaefoCec"); 43 | * 44 | * function onError(e:YouTubeLinkParserEvent):void 45 | * { 46 | * // removing listeners just for clean cosing reasons! 47 | * _ytParser.removeEventListener(YouTubeLinkParserEvent.COMPLETE, onComplete); 48 | * _ytParser.removeEventListener(YouTubeLinkParserEvent.ERROR, onError); 49 | * 50 | * trace("Error: " + e.param.msg); 51 | * } 52 | * 53 | * function onComplete(e:YouTubeLinkParserEvent):void 54 | * { 55 | * // removing listeners just for clean coding reasons! 56 | * _ytParser.removeEventListener(YouTubeLinkParserEvent.COMPLETE, onComplete); 57 | * _ytParser.removeEventListener(YouTubeLinkParserEvent.ERROR, onError); 58 | * 59 | * trace("youTube parse completed..."); 60 | * trace("video thumb: " + _ytParser.thumb); 61 | * trace("video title: " + _ytParser.title); 62 | * trace("possible found videos: " + _ytParser.videoFormats.length); 63 | * 64 | * trace("you can only access youtube public videos... no age restriction for example!"); 65 | * trace("some video formats may be null so you should check their availablily..."); 66 | * trace("to make your job easier, I built another method called getHeaders() which will load video headers for you! 67 | * you can know the video size using these header information :) ") 68 | * 69 | * // let's find the VideoType.VIDEO_MP4 video format in VideoQuality.MEDIUM for this video 70 | * // NOTICE: you should find your own way of selecting a video format! as different videos may not have all formats or qualities available! 71 | * 72 | * var chosenVideo:String; 73 | * for (var i:int = 0; i < _ytParser.videoFormats.length; i++) 74 | * { 75 | * var currVideoData:Object = _ytParser.videoFormats[i]; 76 | * if (currVideoData.mimeType.indexOf(VideoType.VIDEO_MP4) > -1 && currVideoData.quality == VideoQuality.MEDIUM) 77 | * { 78 | * chosenVideo = currVideoData.url; 79 | * break; 80 | * } 81 | * } 82 | * 83 | * _ytParser.addEventListener(YouTubeLinkParserEvent.VIDEO_HEADER_RECEIVED, onHeadersReceived); 84 | * _ytParser.addEventListener(YouTubeLinkParserEvent.VIDEO_HEADER_ERROR, onHeadersError); 85 | * _ytParser.getHeaders(chosenVideo); 86 | * } 87 | * 88 | * function onHeadersError(e:YouTubeLinkParserEvent):void 89 | * { 90 | * _ytParser.removeEventListener(YouTubeLinkParserEvent.VIDEO_HEADER_RECEIVED, onHeadersReceived); 91 | * _ytParser.removeEventListener(YouTubeLinkParserEvent.VIDEO_HEADER_ERROR, onHeadersError); 92 | * 93 | * trace("Error: " + e.param.msg) 94 | * } 95 | * 96 | * function onHeadersReceived(event:YouTubeLinkParserEvent):void 97 | * { 98 | * _ytParser.removeEventListener(YouTubeLinkParserEvent.VIDEO_HEADER_RECEIVED, onHeadersReceived); 99 | * _ytParser.removeEventListener(YouTubeLinkParserEvent.VIDEO_HEADER_ERROR, onHeadersError); 100 | * 101 | * var lng:int = event.param.headers.length; 102 | * var i:int; 103 | * var currHeader:*; 104 | * 105 | * for (i = 0; i < lng; i++ ) 106 | * { 107 | * currHeader = event.param.headers[i]; 108 | * trace(currHeader.name + " = " + currHeader.value); 109 | * } 110 | * 111 | * // ok, we are happy! now let's download this video, like any other file you would download: 112 | * download(event.param.url); 113 | * } 114 | * 115 | * 116 | * @author Hadi Tavakoli - 11/6/2014 6:14 PM 117 | */ 118 | public class YouTubeLinkParser extends EventDispatcher 119 | { 120 | private static const USER_AGENT:String = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:8.0.1)"; 121 | private var _thumbLink:String; 122 | private var _videoTitle:String; 123 | private var _videoFormats:Array; 124 | 125 | public function YouTubeLinkParser():void 126 | { 127 | 128 | } 129 | 130 | // -------------------------------------------------------------------------------------- functions 131 | 132 | private function onHttpResponse(e:HTTPStatusEvent):void 133 | { 134 | 135 | } 136 | 137 | private function onRawDataReceived(e:Event):void 138 | { 139 | var i:int; 140 | var vars:URLVariables; 141 | var obj:Object; 142 | var loader:URLLoader = e.target as URLLoader; 143 | 144 | // remove listeners for this request 145 | loader.removeEventListener(Event.COMPLETE, onRawDataReceived); 146 | loader.removeEventListener(IOErrorEvent.IO_ERROR, onFailure); 147 | loader.removeEventListener(HTTPStatusEvent.HTTP_RESPONSE_STATUS, onHttpResponse); 148 | 149 | var videoVars:URLVariables = loader.data; 150 | _videoFormats = []; 151 | var player_response:Object = JSON.parse(videoVars.player_response); 152 | var formats:Array = player_response.streamingData.formats; 153 | var adaptiveFormats:Array = player_response.streamingData.adaptiveFormats; 154 | 155 | for (i = 0; i < formats.length; i++) 156 | { 157 | obj = formats[i]; 158 | _videoFormats.push(obj); 159 | } 160 | 161 | for (i = 0; i < adaptiveFormats.length; i++) 162 | { 163 | obj = adaptiveFormats[i]; 164 | _videoFormats.push(obj); 165 | } 166 | 167 | _videoTitle = player_response.videoDetails.title; 168 | _thumbLink = player_response.videoDetails.thumbnail.thumbnails[0].url; 169 | 170 | dispatchEvent(new YouTubeLinkParserEvent(YouTubeLinkParserEvent.COMPLETE, _videoFormats)); 171 | } 172 | 173 | private function onFailure(e:IOErrorEvent):void 174 | { 175 | var loader:URLLoader = e.target as URLLoader; 176 | 177 | // remove listeners for this request 178 | loader.removeEventListener(Event.COMPLETE, onRawDataReceived); 179 | loader.removeEventListener(IOErrorEvent.IO_ERROR, onFailure); 180 | loader.removeEventListener(HTTPStatusEvent.HTTP_RESPONSE_STATUS, onHttpResponse); 181 | 182 | dispatchEvent(new YouTubeLinkParserEvent(YouTubeLinkParserEvent.ERROR, {msg:"could not connect to server!"} )); 183 | } 184 | 185 | private function extractYoutubeId($link:String):String 186 | { 187 | var id:String; 188 | 189 | var pattern:RegExp = /v=/i; 190 | if (!pattern.test($link)) return null; 191 | id = $link.substr($link.search(pattern) + 2, 11); 192 | if (id.indexOf("&") > -1) return null; 193 | 194 | return id; 195 | } 196 | 197 | 198 | 199 | // -------------------------------------------------------------------------------------- Methods 200 | 201 | /** 202 | * Pass in a standard youtube link to start parsing the link. watch out for the listeners to see the results. 203 | * 204 | * @param $youtubeUrl 205 | */ 206 | public function parse($youtubeUrl:String):void 207 | { 208 | var videoId:String = extractYoutubeId($youtubeUrl); 209 | 210 | if (!videoId) 211 | { 212 | dispatchEvent(new YouTubeLinkParserEvent(YouTubeLinkParserEvent.ERROR, {msg:"invalid link!"} )); 213 | } 214 | 215 | var vidInfo:String = 'https://www.youtube.com/get_video_info?&video_id=' + videoId + '&asv=3&el=detailpage&hl=en_US'; 216 | 217 | // setup the request method to connect to server 218 | var request:URLRequest = new URLRequest(vidInfo); 219 | request.userAgent = USER_AGENT; 220 | request.method = URLRequestMethod.GET; 221 | 222 | // add listeners and send out the information 223 | var loader:URLLoader = new URLLoader(); 224 | loader.addEventListener(HTTPStatusEvent.HTTP_RESPONSE_STATUS, onHttpResponse); 225 | loader.addEventListener(Event.COMPLETE, onRawDataReceived); 226 | loader.addEventListener(IOErrorEvent.IO_ERROR, onFailure); 227 | loader.dataFormat = URLLoaderDataFormat.VARIABLES; 228 | loader.load(request); 229 | } 230 | 231 | /** 232 | * Use this method to check if this video file is available for download or not. 233 | * add the following listeners to manage it: 234 | * 235 | * @param $url video url retrived from this library 236 | */ 237 | public function getHeaders($url:String):void 238 | { 239 | var request:URLRequest = new URLRequest($url); 240 | request.userAgent = USER_AGENT; 241 | request.method = URLRequestMethod.HEAD; 242 | 243 | var loader:URLLoader = new URLLoader(); 244 | loader.addEventListener(HTTPStatusEvent.HTTP_RESPONSE_STATUS, onHttp); 245 | loader.addEventListener(IOErrorEvent.IO_ERROR, onFailure); 246 | loader.load(request); 247 | 248 | function onFailure(e:IOErrorEvent):void 249 | { 250 | dispatchEvent(new YouTubeLinkParserEvent(YouTubeLinkParserEvent.VIDEO_HEADER_ERROR, {msg:"connection problem or video is not available. try another video format!"} )); 251 | } 252 | 253 | function onHttp(event:HTTPStatusEvent):void 254 | { 255 | dispatchEvent(new YouTubeLinkParserEvent(YouTubeLinkParserEvent.VIDEO_HEADER_RECEIVED, { headers:event.responseHeaders, url:$url } )); 256 | } 257 | } 258 | 259 | // -------------------------------------------------------------------------------------- properties 260 | 261 | public function get videoFormats():Array 262 | { 263 | return _videoFormats; 264 | } 265 | 266 | public function get thumb():String 267 | { 268 | return _thumbLink; 269 | } 270 | 271 | public function get title():String 272 | { 273 | return _videoTitle; 274 | } 275 | 276 | } 277 | } --------------------------------------------------------------------------------