- 2009年3月21日 06:56
- flash
最新はコチラ→ MEMO:Youtube の動画URLを取得する2
Youtube からのFLVをロードする場合は、基本的に以下のURL形式でOK(今はここから更に実際のFLVが置いてある Google にリダイレクトされる)。ロードに必要なトークンは提供されている API などからは参照できないので、みんな本家のソースをスクレイピングして取得してる。本家からはこれからもトークンを参照できると思うので(大幅な設計変更が無ければ...)安全策だと思う。だけど、僕はもうちょっとシンプルに取得したかったので違う方法を探してみた。
http://www.youtube.com/get_video.php?video_id=動画ID&t=トークン
最終的に見つけたのが moringo さんのこの記事。
この頃(2008年の夏)は、外部埋め込み用 swf をロードする際のパラメータにトークンがくっついていたので、Response Headerの Location をわざわざ参照していた。as3 では GET, POST でファイルをロードすることはできても Response Header のみを参照できないので(Web上のみ?)、AVM1 の swf をそのまま AVM2 にロードして、contentLoaderInfo.url から参照してたみたい。結局、as3 だけでは荷が重いので perl でヘッダのみ参照して、トークンを取得することにした。
#!/usr/bin/perl
use strict;
use warnings;
use LWP;
use URI;
use CGI;
my $query = CGI->new;
my $id = $query->param('id');
my $ua = LWP::UserAgent->new();
my $req = HTTP::Request->new(POST => "http://www.youtube.com/v/$id" );
my $res = $ua->request( $req );
my $uri = URI->new($res->header('Location'));
my @pairs = split(/&/, $uri->query);
my ($name, $val);
my %querys = {};
for ( @pairs ) {
($name, $val) = split(/=/, $_);
$querys{ $name } = $val;
}
my $t = $querys{'t'};
print "Content-type: text/plain\n\n";
print "http://www.youtube.com/get_video.php?video_id=$id&t=$t";
適当に名前付けてサーバに設置して、geturl.cgi?id=動画IDでOK。
...だったはずが、その数週間後 Youtube がそれを察知したかのように仕様を変更してくださった。新しい仕様では、以下の形式で youtube から関連情報を参照できて、この中にトークン(token)も含まれていた。
http://www.youtube.com/get_video_info?video_id=動画ID
新しい API 用に修正したのが以下のスクリプト。
#!/usr/bin/perl
use strict;
use warnings;
use LWP;
use URI;
use CGI;
my $query = CGI->new;
my $id = $query->param('id');
my $ua = LWP::UserAgent->new();
my $req = HTTP::Request->new(GET=> "http://www.youtube.com/get_video_info?video_id=$id" );
my $res = $ua->request( $req );
my @pairs = split(/&/,$res->content);
my ($name, $val);
my %querys = {};
for ( @pairs ) {
($name, $val) = split(/=/, $_);
$querys{ $name } = $val;
}
my $t = $querys{'token'};
print "Content-type: text/plain\n\n";
print "http://www.youtube.com/get_video.php?video_id=$id&t=$t\n";
※ちなみに、取得するデータは Content-type が application/x-www-form-urlencoded で、as側でロードする場合は dataFormat を VARIABLES にしてやる必要がある。まぁ、crossdomain.xmlで制限されてるので結局プロキシ書かないと取得できないんだけど...。
→ ローカル限定だけど、as3 のみで Youtube の動画を再生するサンプル。
var loader:URLLoader = new URLLoader();
loader.dataFormat = URLLoaderDataFormat.VARIABLES;
サンプル
さっきのスクリプトを使用してさくっとサンプルを作ってみた。こちら。
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.media.Video;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.net.*;
import flash.events.NetStatusEvent;
import flash.events.SecurityErrorEvent;
import flash.events.AsyncErrorEvent;
public class Youtube extends Sprite
{
protected static const VIDEO_ID:String = "6kxDxLAjkO8"; // 動画ID
protected static const CGI_PATH:String = "http://hogehoge.com/geturl.cgi"; // CGIのパス
private var url:String;
private var variables:URLVariables;
private var video:Video;
private var netStream:NetStream;
private var netConnection:NetConnection;
public function Youtube()
{
connect();
}
private function connect():void {
var request:URLRequest = new URLRequest( CGI_PATH + "?id=" + VIDEO_ID );
request.method = URLRequestMethod.GET;
var yLoader:URLLoader = new URLLoader();
yLoader.dataFormat = URLLoaderDataFormat.TEXT;
yLoader.addEventListener( Event.COMPLETE, loadCompleteHandler );
yLoader.load( request );
}
private function loadCompleteHandler( e:Event ):void {
url = e.target.data.toString();
setVideo();
}
private function setVideo():void {
video = new Video(320, 240);
addChild(video);
netConnection = new NetConnection();
netConnection.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
netConnection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
netConnection.connect(null);
}
private function netStatusHandler(evt:NetStatusEvent):void {
if(evt.info.code == "NetConnection.Connect.Success") {
netStream = new NetStream(netConnection);
netStream.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
netStream.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
netStream.bufferTime = 5;
video.attachNetStream(netStream);
netStream.play( url );
}
}
private function securityErrorHandler(evt:SecurityErrorEvent):void {
trace(evt);
}
private function asyncErrorHandler(evt:AsyncErrorEvent):void {
trace(evt);
}
}
}
まとめ
今のところこの方法で運用して4〜5ヶ月問題は無いけど、Youtube の仕様が変更された場合にいちいち修正が発生するので通常の案件では使えないかもしれない。Youtube から API が正式に提供されない限り、どうやっても邪道な手段をとる以外にないんだけど、確実で安定した方法っていうは無いんだろうか。
- Newer: MEMO:Youtube の動画URLを取得する2
- Older: Refsign Magazine を制作しました