И все-таки заказчики не предсказуемые люди!
Работаешь над проектом, работаешь и все, как кажется, хорошо, а он тебе (заказчик) за неделю до сдачи проекта: «Хочу, чтоб у меня на flash-сайте видео проигрывалось. Да не простое, а с YouTube». Ну и что тут поделаешь… Ничего.
Для уточнения скажу, что сайт сделан в Flash CS4 с использованием Action Script 3.0.
Однако задачка, как оказалось не из легких. И как всегда пришлось выкручиваться.
Спасибо за карму! Перенес статью в блог Adobe Flex.
С чего начать:
Искать статьи на интересующую тему? И что радует таковые имеются, но вот беда, как пишет сам автор, данный способ уже не работает, а обещанного обновления статьи еще нет. Задача…
Можно, конечно, загрузить весь плеер, но поскольку он написан на Action Script 2, то об управлении проигрыванием можно забыть. И даже такое простое действие как масштабирование видео становится, невыносимо, сложной задачей. И так это способ тоже отпадает.
Что же делать? Работать, работать и еще раз работать «И да воздастся тебе за труды твои». И так расписываю решение по шагам:
Первый шаг. Вытаскиваем YouTube плеер из кеш-а браузера (в моем случае браузер FireFox функция about:cache).
Второй шаг. Смотрим код плеера при помощи специальных программ (их много, я использую Sothink SWF Decompiler).
Третий шаг. Ищем в многочисленных классах нужную нам информацию, а именно где и как можно взять прямую ссылку на видео. Из этой статьи мы знаем, что для получения прямой ссылки на видео нам необходимо знать две вещи идентификационный номер видео (video_id) и некий опознавательный знак видео (параметр t). С video_id все просто, он есть в строке запроса видео http://www.youtube.com/watch?v=lIZVEnyHoGU и называется «v». А вот где взять «t», и тут нам помогает разобранный нами на части плеер из которого мы узнаем, что есть некий запрос к серверу с указанием ID видео, при котором мы получаем информацию о нем. Запрос выглядит следующим образом http://youtube.com/get_video_info.php?video_id=lIZVEnyHoGU. Если вставить эту ссылку в строку браузера, то вам предложат скачать файл с названием «get_video_info.php». Скачиваем и смотрим содержимое файла. Файл содержит строку с переменными вида «status=ok&vq=None&author=guylevy… и т.д.». Так вот из всего многообразия переменных нам нужен один он называется «token» — на самом деле это и сеть то самый «t», который мы искали.
Шаг четвертый. Мы добились того, что хотели – получили прямую ссылку на видео и теперь, если сложить все полученное вместе http://www.youtube.com/get_video.php?video_id=lIZVEnyHoGU&t= vjVQa1PpcFNnve5gW7h8B51Da_gr8T6BHzSFMbwf6Dw%3D и, ввести в строку браузера нам предложат скачать flv-ролик с название «video.flv», который в свою очередь можно, либо скачать, либо проиграть в своем плеере.
И так для выполнения третьего шага нам нужно загрузить как текстовый файл «get_video_info.php» и достать из него нужную нам информацию. В этом мне помог класс «URL.as», который я нашел на одном из форумов, он помогает получить практически любую информацию из url-а, а после моей доработки еще и кодировать и декодировать url с закодированными точками, тире, плюсами и т.д.
Весь процесс получения прямой ссылки, я реализовав в классе «YouTubeVideoPlayer.as»
Так же воспользовавшись статьёй про 10 приемов для работы с YouTube в классе «YouTubeVideoPlayer.as» можно устанавливать качество получаемого видео, при условии что таковое имеется.
Собственно вот эти два класса, которые «спасут мир» :)
(Приведенный код написан для использования во Flex)
package com
{
import flash.events.Event;
import flash.net.URLLoader;
import flash.net.URLRequest;
import mx.controls.VideoDisplay;
import mx.events.VideoEvent;
import com.URL;
/**
* Класс YouTubeVideoPlayer.
* @author Aslanyan Tsolak Rubikovich
* @version 1.0
*/
public class YouTubeVideoPlayer extends VideoDisplay
{
private static var GET_VIDEO :String = "http://www.youtube.com/get_video.php";
private static var GET_VIDEO_INFO :String = "http://youtube.com/get_video_info.php";
private static var URL_PREFIX :String = "http://youtube.com?";
private static var QUALITY0 :String = "";
private static var QUALITY1 :String = "&fmt=18";
private static var QUALITY2 :String = "&fmt=22";
private static var QUALITY3 :String = "&ap=%2526fmt%3D18";
private static var QUALITY4 :String = "&ap=%2526fmt%3D22";
private var videoID :String;
private var videoToken :String;
private var videoQuality :String;
private var InfoLoader :URLLoader;
/**
* Конструктор.
* @params source: Строка с идешником вида www.youtube.com/watch?v=lIZVEnyHoGU
* @params quality: Качество проигрываемого видео [0,1,2,3,4]
* @return void
*/
public function YouTubeVideoPlayer(source:String, quality:Number):void
{
super();
videoQuality = DefineQuality(quality);
LoadVideoInfo(source);
}
/**
* Определяет качество воспроизводимого видео.
* @params quality: Качество проигрываемого видео [0,1,2,3,4]
* @return Строку с параметром качества
*/
private function DefineQuality(quality:Number):String
{
switch(quality)
{
case 0: return QUALITY0; break;
case 1: return QUALITY1; break;
case 2: return QUALITY2; break;
case 3: return QUALITY3; break;
case 4: return QUALITY4; break;
}
return QUALITY0;
}
/**
* Инициализируе загрузку информации о видео.
* @params source: Строка с идешником вида www.youtube.com/watch?v=lIZVEnyHoGU
* @return void
*/
private function LoadVideoInfo(source:String):void
{
videoID = GetVideoID(source);
var urlRequest :URLRequest = new URLRequest(GET_VIDEO_INFO + "?video_id=" + videoID);
InfoLoader = new URLLoader();
InfoLoader.addEventListener(Event.COMPLETE, OnInfoLoaderComplete);
InfoLoader.load(urlRequest);
}
/**
* Достает из source VideoID.
* @params source: Строка с идешником вида www.youtube.com/watch?v=lIZVEnyHoGU
* @return VideoID
*/
private function GetVideoID(source:String):String
{
var _url:URL = new URL(source);
return _url.query.parsed.v;
}
/**
* Обработчик окончания загрузки get_video_info.php.
* @return void
*/
private function OnInfoLoaderComplete(e:Event):void
{
InfoLoader.removeEventListener(Event.COMPLETE, OnInfoLoaderComplete);
var str:String = URL_PREFIX + "?" + e.target.data.toString();
videoToken = GetVideoToken(str);
var fullVideoUrl:String = FlvUrlConstruct(videoID, videoToken);
InitVideoDisplay(fullVideoUrl);
}
/**
* Достает из source videoToken.
* @params source: Строка с парамеррами из файла get_video_info.php.
* @return videoToken
*/
private function GetVideoToken(source:String):String
{
var _url:URL = new URL(source);
return _url.query.parsed.token;
}
/**
* Строит прямую URL к видео.
* @params video_id: id видео.
* @params token: id опознавательный знак видео.
* @return videoToken
*/
private function FlvUrlConstruct(video_id:String, token:String):String
{
var fullUrl:String = GET_VIDEO + "?video_id=" + video_id + "&t=" + token + videoQuality;
return fullUrl;
}
/**
* Инициализирует VideoDisplay.
* @params source: ссылка на видео.
* @return void
*/
private function InitVideoDisplay(source:String):void
{
this.autoPlay = true; // автоматическое начало проигрывания
this.source = source; // путь к видео
this.width = 500; // ширина
this.height = 281.25; // высота
this.autoRewind = true; // авто перемотка в начало
this.maintainAspectRatio = true; // задаем способ масштабирования
this.playheadUpdateInterval = 100; // скорость обнавления VideoEvent.PLAYHEAD_UPDATE
this.bufferTime = 5; // задаем интервал буферезации
this.addEventListener(VideoEvent.STATE_CHANGE, StateChange);
}
private function StateChange(e:VideoEvent):void
{
trace(e.state)
}
}
}
и еще один класс
package com
{
/**
* Dschini.org - Manfred Weber
* manfred.dschini.org
* manfred.weber (at) gmail dot com
* Updated Tsolak Aslanyan
* version 1.1
*/
public class URL
{
private static const PATTERN:RegExp = /^([A-Za-z0-9_+.]{1,8}:\/\/)?([!-~]+@)?([^\/?#:]*)(:[0-9]*)?(\/[^?#]*)?(\?[^#]*)?(\#.*)?/i;
private var _url :String;
private var _scheme :String;
private var _userinfo :String;
private var _host :String;
private var _port :String;
private var _path :String;
private var _query :String;
private var _fragment :String;
/**
* Create new URL Object
* @params The url
*/
function URL(url:String):void
{
var result:Array = url.match(URL.PATTERN);
_url = result[0]; // user:pass@example.com:80/foo/bar.php?var1=foo&var2=bar#abc
_scheme = result[1]; // http://
_userinfo = result[2]; // user:pass@
_host = result[3]; // example.com
_port = result[4]; // :80
_path = result[5]; // /foo/bar.php
_query = result[6]; // ?var1=foo&var2=bar
_fragment = result[7]; // #abc
}
/**
* Get the url
*/
public function get url():String
{
return _url.length <= 0 ? undefined : _url;
}
/**
* Get the scheme
*/
public function get scheme():String
{
return _scheme.length <= 0 ? undefined : _scheme.substring(0 , _scheme.length - 3);
}
/**
* Get the userinfo
* Returns an object containing the user and/or password
*/
public function get userinfo():Object
{
var ret:Object = {user:undefined, pass:undefined};
if(_userinfo)
{
var arr:Array = _userinfo.substring(0, _userinfo.length - 1).split(':');
ret.user = arr[0] ? arr[0] : ret.user;
ret.pass = arr[1] ? arr[1] : ret.pas;
}
return ret;
}
/**
* Get the host
*/
public function get host():String
{
return _host.length <= 0 ? undefined : _host;
}
/**
* Get the port
*/
public function get port():int
{
return _port.length <= 0 ? undefined : int(_port.substring(1, _port.length));
}
/**
* Get the path
*/
public function get path():String
{
return _path.length <= 0 ? undefined : _path;
}
/**
* Get the query
* Returns an object containing the raw and parsed query string
*/
public function get query():Object
{
var ret:Object = {raw:undefined, parsed:undefined};
if(_query && _query.length > 0)
{
ret.raw = _query;
var _parse :String = _query.substring(1, _query.length);
var _intovars :Array = _parse.split("&");
ret.parsed = _intovars.length > 0 ? {} : undefined;
for(var i:int = 0; i < _intovars.length; i++)
{
var _kv:Array = _intovars[i].split("=");
ret.parsed[_kv[0]] = _kv[1];
}
}
return ret;
}
/**
* Get the fragment
*/
public function get fragment():String
{
return _fragment.length <= 0 ? undefined : _fragment;
}
/**
* Accepts an encoded string.
* Returns the decoded string.
*/
public function Unescape(value:String):String
{
return unescape(value);
}
/**
* Accepts an decoded string.
* Returns the encoded string.
*/
public function Escape(value:String):String
{
return escape(value);
}
}
}
Вот пример использования классов в Flex Builder:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script><![CDATA[
import com.YouTubeVideoPlayer;
private var vp:YouTubeVideoPlayer;
private function GetVideo():void
{
if(vp != null)
{
vp.stop();
vp.close();
removeChild(vp);
}
var vp:YouTubeVideoPlayer = new YouTubeVideoPlayer(utUrl.text, 1);
vp.x = 119;
vp.y = 38;
addChild(vp);
}
]]></mx:Script>
<mx:Label x="10" y="12" text="Ссылка на видео"/>
<mx:TextInput id="utUrl" x="119" y="10" width="600" text="http://www.youtube.com/watch?v=lIZVEnyHoGU"/>
<mx:Button x="727" y="10" label="Загрузить" click="GetVideo()"/>
</mx:Application>
Надеюсь, данная статья кому-нибудь поможет или хотя бы покажется интересной.
P.S.
Огромное спасибо ayurganov за инвайт!!!