├── 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 | }
--------------------------------------------------------------------------------