269 читателей, 13 постов
Администрация
Модераторы
Разработка приложений с использованием Adobe 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);
}
}
}
<?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>
комментарии (42)
Раз вытянули — значит хотите воспроизводить на своем сайте, через свой плеер, но как вы обошли crossdomain.xml?
Согласно политики безопасности флеша, для того чтобы флеш утянул какой-то файл с другого домена (отличного от местоположения самой флешки) — в корне сайта должен лежать crossdomain.xml.
www.youtube.com/crossdomain.xml смотрим исходный код и видим, что это право дано только некоторым доменам.
<?xml version=«1.0»?>
<!DOCTYPE cross-domain-policy SYSTEM «www.macromedia.com/xml/dtds/cross-domain-policy.dtd»>
<cross-domain-policy>
<allow-access-from domain="*.youtube.com" />
<allow-access-from domain="*.ytimg.com" />
</cross-domain-policy>
Т.е. если мы вставим свой плеер и засунем туда ссылку на флеш ролик с ЮТуба — то ничего не получим. Тогда зачем вытаскивать ссылку? Не проще вставить готовый код плеера ЮТуба?
По поводу вставить готовый код плеера. Это сделать можно, но как быть с тем что сайт сделан на Flash целиком (причем на AS 3.0)? Но и на этот вопрос есть ответ, «Можно», но с ограничениями после вставки плеера в flash-сайт мы не сможем управлять настройками плеера. И это еще пол беды, при такой вставке youtube отдает только один поток, т.е. если делать видео галерею у себя на flash-сайте первый полученный поток остается последним, поскольку нет возможности передать в загруженный плеер команду «NetStream.close()».
Пример схемы:
RSS новость на сайте habra.ru (лежит на habra.ru)
PHP скрипт загружает habra ленту (можно просто file('habra.ru/lenta.php') ) и потом по строкам вывести весь контент. (echo ) — скрипт лежит уже на том сайте, где лежит наша флешка.
Flash грузит наш PHP скрипт. В итоге — мы не нарушает политику Flash'а, т.к. загружаем данные со своего же хоста.
С видео, я думаю такое не получиться, слишком большой объем вывода информации.
А crossdomain больше никак не обойти. Тема изъезжана на любом флешовском портале.
Вопрос в другом — зачем вообще Ютуб?!
Не проще ли сделать аплоад видео на вашем сайте, и загружать ролики непосредственно к вам на сервер? Или там ооооочень мало места? :).
Я понимаю, если б вы делали анонс Ютуба, и хотели сделать загрузку например ТОП50 с Ютуба. Но вы же наверяка хотите какие-то определенные ролики? Поэтому я считаю лучше всего сделать аплоад на сервер свой.
А вот по поводу примера с RSS и PHP это очень интересно, может удастся придумать что-то подобное. Спасибо.
Цитата (http://livedocs.adobe.com/flex/3/html/help.html?content=05B_Security_01.html):
Loading sound and videos
All SWF files, other than those in the local-with-filesystem sandbox, are allowed to load sound and video from network origins, using the Sound.load(), NetConnection.connect(), and NetStream.play() methods.
Эта версия рабочая?
Я тоже не использовал эту либу когда вытаскивал видео, делал сам. Но потом YouTube прикрыли лавочку.
Ютубовским as2 плеером можно управлять через LocalConnection!
Есть специальный api-плеер без контролов youtube.com/apiplayer и сам api: code.google.com/intl/ru/apis/youtube/flash_api_reference.html
1. реализуйте свой сервер например на том же php и api к нему по http — из одного метода — с помощью которого ваша флешка будет общаться с сервером
2. флешка делает один запрос своему серверу передавая — открытый урл видео www.youtube.com/watch?v=lIZVEnyHoGU
3. сервер получив этот урл, выполняет всю вышеописанную вам логику по получении видео контента — и стартует wgetом закачку в свой сторедж, причем ассинхронно — чтобы не ждать окончания закачки — на пыхе это что то типа exec('load.php &') — те отдельным процессом,
load.php:
exec('wget -O /cache/video_timelabel.flv 'http://www.youtube.com/get_video.php?video_id=lIZVEnyHoGU&t= vjVQa1PpcFNnve5gW7h8B51Da_gr8T6BHzSFMbwf6Dw%3D'')
а обратно флешке сервер будет возвращать урл на видео со своего сервера, что то типа myhost.com/cache/video_timelabel.flv
4. флешка воспроизводит видео контент со своего сервера, который может параллельно докачиваться, соответственно перемотка вряд ли будет доступна.
Останется по максиму оптимизировать 3 шаг, но там по сути один дополнительный запрос к ютубу на получения инфы ролика + старт закачки, но по сути нормальный сервак с вменяемым каналом должен обрабатывать такие запросы шустро, так пользователь не заметить тормоза.
exec('wget -O /cache/video_timelabel.flv 'http://www.youtube.com/get_video.php?video_id=lIZVEnyHoGU&t= vjVQa1PpcFNnve5gW7h8B51Da_gr8T6BHzSFMbwf6Dw%3D'')
это значит что в один и тот же файл, будет грузиться заново файл. Значит нужно назначать новое имя для каждой загрузки — а это значит будет больше места занимать. Значит после загрузок нужно чистить кэш — т.к. хранить видео у них нет места. А если чистить кэш каждый раз после просмотра — тогда смысл в этом?
Схема будет выглядеть так — зашол юзер на сайт. Начал смотреть ролик. Он начал грузиться на сервер с Ютуба, потом после просмотра — удаляется. Это жесть :).
1. отдельный процесс — wget качающий бинарный файл — это не тяжелый процесс — их может быть тысячи и сервер на пингвине будет прекрасно себя чувствовать
2. один и тот же ролик не будет качаться заново если реализовать кеширование — в виде хеш таблицы — где будут храниться преобразования — входного и выходного урла.
3. чистку старых файлов можно сделать сколь угодно долгой, не обязательно сутки, хотя бы 3 суток и вероятность одновременной закачки одного файла исключена.
а вообще если вы вы за красоту решений, то в разработке на флеше вам делать нечего. Флеш платформа это сурогат, технология как только не эволюционировала, уже не понятно куда флешь развивается, хотя flex фреймворк обнадеживает, но девелопмент под флеш это перманентное изъебство даже в самых тривиальных задачах — это участь всех flex разработчиков., нужно смерится.
Прежде чем утверждать «Вы не поинтересовались этим вопросам» нужно сначала спросить интересовался я или нет.
Еще один момент, код полностью рабочий, только работает в тестовом режиме (во Flex или Flash), когда flash player не проверяет безопасность и наличие crossdomain.xml.
И последнее. Статья писалась именно для того чтоб ее читали и высказывали свое, конструктивное, мнение. Основанное на фактах, а не наличных представлениях об истине.
P.S. Но и ваш комментарий заслуживает должного внимания при правильном его прочтении (между строк).
Возможно вам поможет эта статья:
blog.imagzstudio.com/2008/10/15/working-with-youtube-api-in-flex/
Похоже политика ютуба поменялась, и для использования апи теперь нужен developer key.
Впрочем его легко получить зарегистрировавшись в «YouTube APIs and Tools».
Инструменты и API YouTube — читаем здесь:
code.google.com/intl/ru-RU/apis/youtube/overview.html
я сам не спец, напарник0флешер просит ещё :)