YouTubeダウンロードスクリプトのプレイリスト対応
YouTubeにはplaylistというものができています。クリックすると、直接指定されている動画から始まり、それに続くリストが順番に自動再生されるものですが、ありがた迷惑のような気もします。ただ、YouTubeのダウンローダーページでも、プレイリストのURLを指定してエラーしている方もいらっしゃいますので、対応することにしました。
対応は2レベルに分れます。
- レベル1: 直接指定された動画のダウンロードができる。
- レベル2: プレイリストに指定されている動画が全部ダウンロードできる。
プレイリストは8曲くらい連なっているようでダウンロードには多大な時間が掛かりますため、バッチ処理になるスクリプト(ytdownloader.pl)ではレベル2対応、ウェブサービス(YouTubeダウンローダ)ではレベル1対応を行いました。
仕様の解析
プレイリストは以下のようなURL形式です。
http://www.youtube.com/view_play_list?p=3A7DF57CA0B7E08B&playnext=1&playnext_from=PL&v=n3m1P05Shy4
ページを開くと、直接ビデオが始まり、リストのビデオがその後が続きます。
直接始まるビデオはこちらに書いている通り、"var swfArgs"から取得することができます。これを取得するまでが"レベル1"ということになります。
次にプレイリストの続きはどうなっているでしょうか。
<div id="playlistRow_PL_23" class=" watch-playlist-row v*CGaJrUXe45A ">
<a href="/watch?v=CGaJrUXe45A&feature=PlayList&p=BB482CF432E45110&index=23"
class="watch-playlist-row-link">
これがプレイリスト23番目で、赤字がビデオ識別子になります。(2箇所に指定されています)。直接指定されていたのが、このプレイリストの22番目のようで、その後は上記のブロックが順番に並んでいます。従って、これを順番に取得してやれば良いことになります。
それにしても、他の情報も同様なのですが、ページ中の情報には冗長なものがすごく多いです。なんだかきちんと設計していないんじゃないかという感じがしてしまいます。実際にはビデオストリーム(100MBにもなる)に比べれば誤差の範囲なので、性能の問題にはなりませんが。
実装
完成したスクリプトはこちらにあります。"-p"オプションを指定するとプレイリストのファイルを全部取得します。
さて、最初にプレイリストをリストに格納します。上記のどちらを使っても良いのですが、上のを使います。
} elsif (m!<div\s+id=\"playlistRow.*\"\s+class=\"\s*watch-playlist-row\s+v*([^\s\"]+)\s*\">!) {
push(@vplaylist, "http://www.youtube.com/watch?v=${1}");
}
次に実際の中身を取得します。
foreach (@vplaylist) {
$pid = fork();
if ($pid == 0) {
#--- child process
$Pcmd = "ytdownloader.pl $Options \'$_\'";
exec("$Pcmd");
} else {
push(@pids, $pid);
}
}
foreach (@pids) {
waitpid($_, 0);
}
自分を呼び出してダウンロードします。ここでは並行してダウンロードしたかったのでforkしていますが、行儀良くシーケンシャルにやるのでしたら、systemで良いでしょう。また、waitpidしていますが必須なわけではありません。