Kjetil's Information Center: A Blog About My Projects

Perl YouTube Fetcher

I have created a small Perl script to fetch (download) videos from YouTube. Simply supply the video URL as an argument to the script, and the video file will be downloaded to the current directory.

In order to run the script, you need to have the LWP::UserAgent module from libwww-perl installed. It will hopefully function as long as they do not decide to do any major changes to the YouTube website.

The final video file should be viewable with media players like MPlayer.

#!/usr/bin/perl -w
use strict;
use LWP::UserAgent;

my $url = shift @ARGV
  or die "Usage: $0 <youtube URL>\n";

my $browser = LWP::UserAgent->new;

my $response = $browser->get($url);
die "Failed to get URL: $url [" . $response->status_line . "]\n"
  unless $response->is_success;

(my $id = $url) =~ s/^.*?v=(.*)$/$1/; # Get the youtube ID.

# Attempt to locate the flash arguments in the content.
my $forged_url;
if ($response->content =~ m/swfArgs\s*=\s*{([^}]*)}/) {
  # Convert arguments from JSON format to a new forged URL.
  $forged_url = $1;
  $forged_url =~ s/"//g;
  $forged_url =~ s/\s*:\s*/=/g;
  $forged_url =~ s/\s*,\s*/&/g;
  $forged_url = "http://youtube.com/get_video?" . $forged_url;
} else {
  die "Did not find flash arguments.\n";

# Do a HEAD request to find the total size first.
$response = $browser->head($forged_url);
die "Failed to get headers of forged URL: $forged_url [" . 
  $response->status_line . "]\n" unless $response->is_success;
my $size = $response->headers->content_length;

# Fetch all of the video data with a callback to display progress.
my $video;
$response = $browser->get($forged_url, ":content_cb" => \&content_callback);
die "Failed to get forged URL: $forged_url [" . $response->status_line . "]\n"
  unless $response->is_success;

# Write the video data to file.
open WRITEVIDEO, ">$id.flv"
  or die "Could not open file '$id.flv' for writing: $!\n";
print WRITEVIDEO $video;

print STDERR "Saved video to file: $id.flv\n";

sub content_callback
  my($data, $response, $protocol) = @_;
  $video .= $data;
  printf STDERR "%.1f%%\r", (length($video) / $size) * 100;

Topic: Scripts and Code, by Kjetil @ 24/04-2008, Article Link