理系学生日記

おまえはいつまで学生気分なのか

Youtube からのダウンロードとエンコードを Gearman で実行させる

Youtube のビデオを Web アプリからダウンロード・エンコードできるようにしていたのですが、この Web アプリの実装は、ダウンロード及びエンコードの間レスポンスはユーザに返却されないというクソ実装でした。
リクエスト自体は XHR で行うのですが、Catalyst に付いてくる開発用サーバは (たしか) 1 プロセス・1 スレッドで動作します。結果として、ダウンロードとエンコードが完了するまでの間、当該プロセスは占有され、いかなる処理も実施できないという困った状態に陥っていました。

この状況を打破するために、ジョブキューでダウンロード・エンコードを行うようにすることを決意。また、これを機に、Youtube からのダウンローダを独自実装から、xaicron さんの WWW::YouTube::Download に差し換えてみました。

worker 実装は以下の通り。

use strict;
use warnings;
use WWW::YouTube::Download;
use Gearman::Worker;
use Storable qw(thaw);
use Getopt::Long;
use Time::Piece;
use Path::Class;
use IPC::Run3 qw(run3);

GetOptions(
    'from=s' => \my $from,
    'to=s'   => \my $to,
) or die "GetOptions error: @ARGV";

my $ymd = localtime()->ymd;
my $fromdir = dir($from);
my $todir   = dir($to, $ymd);

$todir->mkpath unless -e $todir;

my $dler = WWW::YouTube::Download->new;
my $worker = Gearman::Worker->new;
   $worker->job_servers(qw(localhost));
   $worker->register_function(
       download => sub {
           my $job = shift;
           my $video_id = ${ thaw($job->arg) };

           my $filepath = $fromdir->file($video_id . $dler->get_suffix( $video_id ));

           $dler->download( $video_id => { file_name => $filepath->stringify() });
           encode( $filepath );
       }
    );
$worker->work while 1;

sub encode {
    my ($from_file) = @_;

    -e $from_file or do { warn "$from_file is not found\n";    return; };
    -r _          or do { warn "$from_file is not readable\n"; return; };

    my $to_file   = $todir->file( $from_file->basename() . ".m4a" )->absolute();

    my $cmd = [qw(ffmpeg -i), $from_file, qw(-vn -acodec libfaac -ab 256k -y), $to_file ];
    run3 $cmd, \undef, \undef, \my $err;

    warn "err: $err\n" unless $?;
    return 1 if $? == 0;
}