#!/usr/bin/perl # Filename: myfilm # Author: David Ljung Madison <DaveSource.com> # See License: http://MarginalHacks.com/License/ # Description: Download iFilm/youtube movies given a film ID. # Converts youtube films from flash to .mov if ffmpeg is available. my $VERSION= '1.02'; use strict; ################################################## # Setup the variables ################################################## my $PROGNAME = $0; $PROGNAME =~ s|.*/||; my ($BASENAME,$PROGNAME) = ($0 =~ m|(.*)/(.+)|) ? ($1?$1:'/',$2) : ('.',$0); # This will probably change.. my $IFILM = "http://www.ifilm.com/player/mac.jsp?ifilmId="; my $YOUTUBE = "http://youtube.com/get_video.php?l=165&video_id="; # How to fetch web files - pick one # Actual Safari agent example: # Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/416.12 (KHTML, like Gecko) Safari/416.13 my $GET = "GET -H 'user-agent: Mozilla/5.0'"; my $LYNX = "links -source"; my $GET_URL = $LYNX; my $POST = "mov"; # For converting youtube to .mov my $FFMPEG = "ffmpeg"; ################################################## # Usage ################################################## sub fatal { foreach my $msg (@_) { print STDERR "[$PROGNAME] ERROR: $msg\n"; } exit(-1); } sub usage { foreach my $msg (@_) { print STDERR "ERROR: $msg\n"; } print STDERR <<USAGE; Usage:\t$PROGNAME [-d] <ifilm URL, youtube URL or ifilm id> Download an ifilm movie -o <file> Save to file -bw <56,200,300> Force a bandwidth other than your iFilm settings -d Set debug mode -bg Run ffmpeg in the background USAGE exit -1; } sub parse_args { my $opt = {}; while (my $arg=shift(@ARGV)) { if ($arg =~ /^-h$/) { usage(); } if ($arg =~ /^-d$/) { $MAIN::DEBUG=1; next; } if ($arg =~ /^-bg$/) { $opt->{bg} = 1; next; } if ($arg =~ /^-bw$/) { $opt->{bw} = shift @ARGV; next; } if ($arg =~ /^-o$/) { $opt->{save} = shift @ARGV; next; } if ($arg =~ /^-/) { usage("Unknown option: $arg"); } if ($arg =~ m|^http://([^/]+)/.*?([^/]+)$|) { ($opt->{url}, $opt->{dom}, $opt->{film}) = ($arg, $1, $2); next; } if ($arg =~ /^[0-9]+$/) { ($opt->{arg}, $opt->{dom},$opt->{film}) = ("ifilm.com", $arg); next; } usage("Unknown film id. I need an iFilm or youtube URL"); } usage("No film URL specified.") unless $opt->{film}; $opt; } sub debug { return unless $MAIN::DEBUG; foreach my $msg (@_) { print STDERR "[$PROGNAME] $msg\n"; } } ################################################## # Utils ################################################## sub title_embed { my ($opt, $url) = @_; debug("URL: $url"); my $open = "$GET_URL $url"; debug("GET: $open"); open(FILM,"$open |") || usage("Couldn't get URL: $url\nMake sure [$GET_URL] is installed"); # Find the film location my $dl; my $title; my $bw = $opt->{bw}; my $embed; my $and_t; while(<FILM>) { $dl = $1 if /var\s+dl\s*=\s*"?(-?\d+)"?/; $bw = $1 if !$bw && /var\s+bw\s*=\s*"?(\d+)"?/; $embed = $1 if /<embed.*(http:[^>"]+)/i; $title = $1 if /span class="title">([^<]+)/; $title = $1 if /<title>(.*)<\/title>/; $and_t = $1 if /video_id=.*&t=([^&"]+)/; } close(FILM); ($title,$embed,$dl,$bw,$and_t); } ################################################## # iFilm ################################################## sub iFilm { my ($opt,$film) = @_; print STDERR "Fetching iFilm ID: $film\n"; # Read the ifilm HTML my $url = $IFILM.$film; my ($title,$smil,$dl,$bw) = title_embed($opt, $url); $title = $title || $film; usage("Couldn't find 'dl' key in URL [$url]") unless $dl; usage("Couldn't find 'bw' key in URL [$url]") unless $bw; usage("Couldn't find 'embed' tag in URL [$url]") unless $smil; $smil =~ s/'\s+\+\s+dl\s+\+\s+'/$dl/g; $smil =~ s/'\s+\+\s+bw\s+\+\s+'/$bw/g; $smil =~ s/"$//; debug("SMIL: $smil"); # Get the smil file my $open = "$GET_URL \Q$smil\E"; open(SMIL,"$open |") || usage("Couldn't get SMIL: $smil"); my $video; while (<SMIL>) { $video = $1 if /video src="(http[^"]+)"/; } usage("Couldn't find video source key in SMIL [$smil]") unless $video; close SMIL; debug("Found: $video"); $title =~ s/[:\s]+/_/g; my $save = $opt->{save} || "$title.$POST"; $save =~ s|/|_of_|g; print "Saving to: $save\n"; debug("$GET_URL \Q$video\E > \Q$save\E\n"); system("$GET_URL \Q$video\E > \Q$save\E"); } ################################################## # youtube ################################################## sub youtube { my ($opt, $film) = @_; print STDERR "Fetching youtube ID: $film\n"; # Get name my ($save,undef,undef,undef,$and_t) = title_embed($opt, $opt->{url}); fatal("No '&t=' code found for youtube [$opt->{url}]") unless $and_t; if ($opt->{save}) { $save = $opt->{save}; } else { fatal("No title found for youtube [$opt->{url}]") unless $save; $save =~ s/youtube( - )?(.+)/$2/ig; print STDERR "Found film '$save'\n"; $save =~ s/[:\s]+/_/g; } $save =~ s/(\.flv)?$/.flv/; $save =~ s|/|_of_|g; $save =~ s/[\*\?\'\"]//g; # Get rid of troublesome characters. # ffmpeg gets confused easily # Get video #print "TITLE; $title -> $save\n"; my $video = $YOUTUBE.$film."&t=$and_t"; debug("Video: $video"); print "Saving to: $save\n"; system("$GET_URL \Q$video\E > \Q$save\E"); # Attempt convert to .mov my $mov = $save; $mov =~ s/\.flv$/.mov/; print "Convert to: $mov\n"; exec($FFMPEG,"-v",0,"-i",$save,$mov) unless fork; if ($opt->{bg}) { sleep 1; # To let the intial ffmpeg output go through.. } else { wait; } } ################################################## # Main code ################################################## sub main { my $opt = parse_args(); if ($opt->{dom} =~ /youtube/i) { $opt->{film} = "watch?v=$opt->{film}" if $opt->{url} =~ s|www.youtube.com/v/|www.youtube.com/watch?v=|; usage("Couldn't find youtube video (URL must contain 'v=[video_id]')") unless $opt->{film} =~ /v=([a-z0-9_-]+)/i; return youtube($opt, $1); } if ($opt->{dom} =~ /ifilm/i) { usage("Couldn't find iFilm video (URL must contain a number/film id)") unless $opt->{film} =~ /([0-9]+)/; return iFilm($opt, $1); } } main();