July 14, 2009

Shell may be good enough to convert a Youtube video, but to download it you need Perl

I was writing a little in my blog about shell. Shell comes in handy for very short scripts, or for admin work, or for tasks where you can't expect anything else to be available. What next is available for one programmer on Linux? Next we have Perl. Much richer than shell, modular with enormous archive of reusable packages called CPAN. It is highly unlikely that it is not included in your distro by default, it is impossible that it can not be installed. If you are dealing with a task which exceeds capabilities of shell, Pearl is most likely to do the job.

For the start – the usual Hello World:

perl -e 'print "Hello World!
"'

Switch -e says to perl to execute what follows; the code we place in single quotes. The thing which I forgot to tell you in my very short story about shell is that you can actually ask Linux for the location of sh, bash, perl, python or java, like this:

which perl

and it will tell you where it is. To create script and make it executable, the procedure is the same as with shell script. The story about 'r' is also valid, if you are copying code from somewhere.

Now, instead of trying to create yet another tutorial of type this is string and this is if statement, I will go directly to a very important task from real life. That is naturally download FLV from Youtube, convert it to MP4 and play it on your Nokia N95 (or some other mobile device).

For example, you can use bash script to convert all videos in some directory into MP4 videos playable on Nokia N95 phone. This one uses ffmpeg:

#!/bin/bash
# usage sh flv2mp4_1.sh ~/tmp/*
# this should do for Nokia N95 and similar
for i in $@; do
    ffmpeg -i $i -f mp4 -vcodec mpeg4 -b 262144 -s 320x240 -acodec aac -ar 48000 -ab 96000 -ac 2 -r 15 $i.mp4
done 

and this one mencoder:

#!/bin/bash
# usage sh flv2mp4_1.sh ~/tmp/*
# this should do for Nokia N95 and similar
    mencoder -of lavf -lavfopts format=mp4 -oac lavc -ovc lavc -lavcopts vbitrate=256:aglobal=1:vglobal=1:acodec=libfaac:abitrate=64:vcodec=mpeg4:keyint=25 -ofps 15 -af lavcresample=44100 -vf harddup,scale=320:240 -mc 0 $i -o $i.mp4
done

You will need to install those encoders if they are not available on your machine. Now we may run into problems like 'path contains spaces' but that is where bash should be enough. When did you see videos from Youtube with spaces in the name? If we need to download videos from Youtube then bash may not be the best tool for the job. Now what comes into play is CPAN which is enormous and there we can find just about anything. Hironori Yoshida contributed WebService::YouTube::Videos - Perl interface to youtube videos.

This is the procedure to install WebService::YouTube

sudo perl -MCPAN -e shell

Super user is required to do that, slightly different shell will appear, now from CPAN shell we update our CPAN

cpan[1]> install Bundle::CPAN

but only for the first time, later we will go right to installing the desired package. That may take some time.

cpan[2]> reload cpan

And finally why we started all this:

cpan[3]> install WebService::YouTube

It may be needed to install dependencies, like XML::Simple, CPAN will suggest that it downloads and installs dependencies. On my box additional dependencies were required, not detected by CPAN and those are XML::SAX with XML::NamespaceSupport. Namely, during the execution of the test recently_added.pl from WebService::YouTube, Perl reported a pile of errors and suggestion to install SAX. That was the error message:

'nsexpand' option requires XML::SAX at /usr/local/share/perl/5.10.0/WebService/YouTube/Feeds.pm line 61

Use of uninitialized value $id in substitution (s///) at /usr/local/share/perl/5.10.0/WebService/YouTube/Feeds.pm line 81.

Without further ado here is the downloader:

#!/usr/bin/perl -w
use strict;
use WebService::YouTube::Util;

my $numArgs = $#ARGV + 1;
if ($numArgs<2){
    print "Usage: ./$0 videoID name2save";
    exit;
}
my ($video, $infile);
$infile = "$ENV{HOME}/$ARGV[1]";
open(IN,"> $infile") || die "$_
";
$video = WebService::YouTube::Util->get_video($ARGV[0]);
print IN $video;
close IN || die "$_
";

Copy, save, chmod to executable. Pass to it video ID and where to save it. For example:

./youtubevideo.pl yg2hXOovRDc test.flv

There are a few things which one may wish to add to the download process. For example callback so that we know how far we are with the download.

Curious to know how it works? Here is the link http://search.cpan.org/CPAN/authors/id/Y/YO/YOSHIDA/WebService-YouTube-1.0.3.tar.gz

OK enough for now, maybe next time I will take a look at callback.

 

Click Here!