YouTubeダウンローダを最新仕様に更新
YouTubeダウンロードサービスとダウンロードスクリプトがエラーしていたのを修正しました。
Apacheのログを見る限りでは、8/4にYouTubeの仕様が変更されており、ダウンロードが一切できなくなっていました。別の仕事の納期が迫っていたために放置していたのですが、今週頭になんとか上がり、2〜3日前から見ていました。昨日、ようやく最新仕様に合わせて動かすことができるようになりました。
変更点は、以下のようなものでした。
- fmt_url_mapがなくなって、url_encoded_fmt_stream_mapになった。
- この記述があるタグも変わり、<embed type="application/x-shockwave-flash"とyt.setConfig({ 'PLAYER_CONFIG':の二箇所になった。後者は初顔。
webmコーデックのファイル(43,44,45)が新登場。
url_encoded_fmt_stream_mapは以前と書式が異なります。今回は、itagでフォーマットタイプを知り、url=http://....でURLを得ます。楽勝と思い実装してみたのですが、なぜか上手く行きません。やみくもにやっていては駄目と思われましたので、少し分析してみました。
はまってしまっていた罠は、以下の2点のようでした。
- <embed ...のほうは、urlエンコードが2回掛かっています。また、PLAYER_CONFIGのほうは、urlエンコードが1回と、Unicode Escape(一部文字に)が1回掛かっています。
- webmフォーマットのファイルはurlパラメタそのままで取得できますが、他のフォーマット(mp4/flv)は、quality=以下を切ってやらないと"403 Forbidden"になってしまいます。
(1)はプレイヤへの引数渡しの際の禁止文字の都合なのだろうと推測できますが、元々のパラメタが呪文文字列(数字)なので、結構悩みました。(2)に関しては全く見当が付きません。処置が的確なのかどうかすら分らない状態です。
結論
このコードで動いています。(embed, PLAYER_CONFIGのどちらでも動きます)
if (m|\'PLAYER_CONFIG\': {(.*)}$|) {
my $rt = GetFmtUrlMapFromPlayerConfig($1);
%vphash = %$rt;
if (length($title) <= 0) {
$title = GetTitleFromPlayerConfig($1);
}
} elsif (m|<embed type=\"application/x-shockwave-flash\" +([^>]*)>|) {
my $rt = GetFmtUrlMapFromEmbed($1);
%vphash = %$rt;
if (length($title) <= 0) {
$title = GetTitleFromEmbed($1);
}
}
sub GetFmtUrlMapFromEmbed
{
my ($argstr) = @_;
if ($argstr =~ m|;fmt_url_map=([^;\"]+)[;\"]|) {
my $ar = urldecode($1);
my @vpbacks = split(/,/, $ar);
foreach my $vpback (@vpbacks) {
$vpback =~ m/^[\s]*([0-9]*)|(.*)[\s]*$/;
$vphsh{$1} = $2;
}
} elsif ($argstr =~ m|url_encoded_fmt_stream_map=([^\;]+)\;|) {
my $ar = urldecode($1);
my @prms = split(/,/, $ar);
foreach my $prm (@prms) {
if ($prm =~ m!itag=([0-9]+)[^0-9]*!) {
my $fmt = $1;
my $ar = urldecode($prm);
if ($ar =~ m!url=([^\;]+)!) {
$ar = $1;
if ($fmt < 40) {
#-- not webm
$ar =~ s!&quality=.*!!;
}
$vphsh{$fmt} = $ar;
}
}
}
}
return %vphsh;
}
sub GetFmtUrlMapFromPlayerConfig
{
my ($argstr) = @_;
if ($argstr =~ m|\"url_encoded_fmt_stream_map\": \"([^\"]+)\"|) {
my @prms = split(/,/, $1);
foreach my $prm (@prms) {
if ($prm =~ m!itag=([0-9]+)[^0-9]*!) {
my $fmt = $1;
my $ar = UnicodeUnescape(urldecode($prm));
if ($ar =~ m!url=([^\;]+)!) {
$ar = $1;
if ($fmt < 40) {
#-- not webm
$ar =~ s!&quality=.*!!;
}
$vphsh{$fmt} = $ar;
}
}
}
}
return %vphsh;
}
れで動くようになりましたが、webm対応ができていません。今後、主力のコーデックになるようなので、将来的には対応は必須なのでしょう。しかし、現在はバックワードコンパチビリティのためのmp4はおろか、flvまで用意されているようですので、後回しにします。というのも、コーデックが新しいので、サーバで使っているシステムに導入するのは骨なのです。それに、性能の上では圧倒的に優れている、という訳でもなさそうなことも一因です。
しかし、政治的な観点では、ライセンスフリーのwebmは大歓迎ですので、なんとかしたいものです。GIFやMP3で繰り返された混乱は、サービスを提供する側からすると大きな障害になってしまいますので。