#!/usr/bin/perl
#
# 2007.07.30
#
# EMP
#
#
use constant VERSION => "1";
use strict;
use warnings;
use LWP::UserAgent;
use Getopt::Std;
$Getopt::Std::STANDARD_HELP_VERSION = 1;
# set these values for default -l (login) and -p (pass) values
#
use constant YT_LOGIN => "";
use constant YT_PASS => "";
# video categories
#
my %cats = (
2 => 'Autos & Vehicles',
23 => 'Comedy',
24 => 'Entertainment',
1 => 'Film & Animation',
20 => 'Gadgets & Games',
26 => 'Howto & DIY',
10 => 'Music',
25 => 'News & Politics',
22 => 'People & Blogs',
15 => 'Pets & Animals',
17 => 'Sports',
19 => 'Travel & Places'
);
# various urls
my $login_url = 'http://www.youtube.com/login';
my $upload_url = 'http://www.youtube.com/my_videos_upload';
unless (@ARGV) {
HELP_MESSAGE();
}
my %opts;
getopts('l:p:f:c:t:d:x:', \%opts);
# if -l or -p are not given, try to use YT_LOGIN and YT_PASS constants
preamble();
print "Youtube username/login as neither defined nor passed as an argument\n";
print "Use -l switch to specify the username\n";
print "Example: -l joe_random\n";
}
else {
$opts{l} = YT_LOGIN;
}
}
preamble();
print "Password was neither defined nor passed as an argument\n";
print "Use -p switch to specify the password\n";
print "Example: -p secretPass\n";
}
else {
$opts{p} = YT_PASS;
}
}
preamble();
print "No video file was specified\n";
print "Use -f switch to specify the video file\n";
print 'Example: -f "C:\Program Files\movie.avi"',
"\n";
print 'Example: -f "/home/pkrumins/super.cool.video.wmv"',
"\n";
}
unless (-r $opts{f}) {
preamble();
print "Video file is not readable or does not exist\n";
print "Check the permissions and the path to the file\n";
}
preamble();
print "No video category was specified\n";
print "Use -c switch to set the category of the video\n";
print "Example: -c 20, would set category to Gadgets & Games\n\n";
print_cats();
}
preamble();
print "Category '$opts{c}' does not exist\n\n";
print_cats();
}
preamble();
print "No video title was specified\n";
print "Use -t switch to set the title of the video\n";
print 'Example: -t "Super Cool Video Title"',
"\n";
}
preamble();
print "No video description was specified\n";
print "Use -d switch to set the description of the video\n";
print 'Example: -d "The coolest video description"',
"\n";
}
preamble();
print "No tags were specified\n";
print "Use -x switch to set the tags\n";
print 'Example: -x "foo, bar, baz, hacker, purl"',
"\n";
}
# tags should be at least two chars, can't be just numbers
my @tags =
split /,\
s+/,
$opts{x
};
my @filtered_tags =
grep { length >
2 && !/^\d+$/
} @tags;
unless (@filtered_tags) {
preamble();
print "Tags must at least two chars in length and must not be numeric!\n";
print "For example, 'foo', 'bar', 'yo29' are valid tags, but ";
print "'22222', 'hi', 'b9' are invalid\n";
}
$opts{x
} =
join ', ',
@filtered_tags;
# create the user agent, have it store cookies and
# pretend to be a cool windows firefox browser
my $ua = LWP::UserAgent->new(
cookie_jar => {},
agent => 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.4) '.
'Gecko/20070515 Firefox/2.0.0.4'
);
# let the user agent follow redirects after a POST request
push @
{$ua->
requests_redirectable},
'POST';
print "Logging in to YouTube...\n";
login();
print "Uploading the video ($opts{t})...\n";
upload();
sub login {
# submit the login form
my $res = $ua->post($login_url,
{
current_form => 'loginForm',
username => $opts{l},
password => $opts{p},
action_login => 'Log In'
}
);
unless ($res->is_success) {
die "Failed logging in: failed submitting login form: ",
$res->status_line;
}
# We have no good way to check if we really logged in.
# I found that when you have logged in the upper right menu changes
# and you have access to 'Log Out' option.
# We check for this string to see if we have logged in.
unless ($res->content =~ /Log Out/) {
die "Failed logging in: username/password incorrect";
}
}
sub upload {
# upload is actually a two step process, first we set the video info,
# and then we post the actual video file
#
my $resp = $ua->get($upload_url);
unless ($resp->is_success) {
die "Failed getting $upload_url: ",
$resp->
status_line;
}
my $session_token = extract_session_token($resp->content);
die "Failed extracting session token, YouTube might have redesigned!";
}
# let's prepare the video field hash which we will need in both steps
my %vid_fields = (
field_myvideo_title => $opts{t},
field_myvideo_descr => $opts{d},
field_myvideo_keywords => $opts{x},
field_myvideo_categories => $opts{c},
language => "EN",
action_upload => "Upload a video...",
allow_embeddings => "Yes",
allow_responses => "Yes",
allow_comments => "Yes",
allow_ratings => "Yes",
location => "",
field_date_mon => 0,
field_date_day => 0,
field_date_yr => 0,
field_privacy => "public",
ignore_broadcast_settings => 0,
session_token => $session_token
);
$resp = upload_step_one(\%vid_fields);
# now add additional video fields, the new session token,
# the addresser field (no idea what it is), and some more fields
$vid_fields{session_token} = extract_session_token($resp->content);
unless (defined $vid_fields{session_token
}) { die "Failed extracting session token for upload step two\n",
"YouTube might have redesigned :(";
}
if ($resp->
content =~
m{name=
"addresser" value=
"(.+?)">
}) { $vid_fields{addresser} = $1;
}
else {
die "Failed extracting 'addresser' id for upload step two\n",
"YouTube might have redesigned :(";
}
$vid_fields{contact} = "";
$vid_fields{field_command} = "myvideo_submit";
$vid_fields{field_uploadfile} = [ $opts{f} ];
$vid_fields{field_private_share_entities} = "";
$vid_fields{action_upload} = "Upload Video";
# the upload form's action URL at step 2 changes, we need to extract it
my $action_url;
if ($resp->
content =~
m{enctype=
"multipart/form-data" action=
"(.+?)"}) { $action_url = $1;
}
else {
die "Failed extracting action URL, YouTube might have redesigned!";
}
$resp = upload_step_two($action_url, \%vid_fields);
# After the video has been uploaded, youtube thanks the user
# for uploading the vid. Let's test for this thanks message
# to see if the upload was successful
unless ($resp->content =~ /Upload Complete/) {
die "Upload might have failed, no 'thanks for upload' message ",
"was found!.\n",
"Or YouTube might have redesigned!";
}
}
sub extract_session_token {
if ($content =~
m{token =
"(.+?)"}) { }
}
sub upload_step_one {
my $resp = $ua->post($upload_url, $vid_fields,
"Content_Type" => "form-data");
unless ($resp->is_success) {
die "First upload step failed: ",
$resp->
status_line;
}
}
sub upload_step_two {
my ($url, $vid_fields) = @_;
my $resp = $ua->post($url, $vid_fields,
"Content_Type" => "form-data");
unless ($resp->is_success) {
die "Second upload step failed: ",
$resp->
status_line;
}
}
sub HELP_MESSAGE {
preamble();
"-l [login] ",
"-p [password] ",
"-f <video file> ",
"-c <category> ",
"-t <title> ",
"-d <description> ",
"-x <comma, separated, tags>\n\n";
print_cats();
}
sub print_cats {
print "Possible categories (for -c switch):\n";
printf "%-4s - %s\n",
$_,
$cats{$_} foreach (sort { $cats{$a} cmp $cats{$b}
}
sub VERSION_MESSAGE {
preamble();
print "Version: v", VERSION,
"\n";
}
sub preamble {
print "\n"
}