1 # -*- coding: ascii -*-
2 ###########################################################################
3 # clive, command line video extraction utility.
5 # Copyright 2009 Toni Gundogdu.
7 # This file is part of clive.
9 # clive is free software: you can redistribute it and/or modify it under
10 # the terms of the GNU General Public License as published by the Free
11 # Software Foundation, either version 3 of the License, or (at your option)
14 # clive is distributed in the hope that it will be useful, but WITHOUT ANY
15 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
19 # You should have received a copy of the GNU General Public License along
20 # with this program. If not, see <http://www.gnu.org/licenses/>.
21 ###########################################################################
27 use base
'Class::Singleton';
29 use WWW
::Curl
::Easy
4.05;
31 use Encode
qw(from_to decode_utf8 FB_CROAK);
33 use clive
::Error
qw(CLIVE_NET CLIVE_STOP);
40 my $config = clive
::Config
->instance->config;
42 my $c = WWW
::Curl
::Easy
->new;
43 $c->setopt( CURLOPT_USERAGENT
, $config->{agent
} || "Mozilla/5.0" );
44 $c->setopt( CURLOPT_FOLLOWLOCATION
, 1 );
45 $c->setopt( CURLOPT_AUTOREFERER
, 1 );
46 $c->setopt( CURLOPT_HEADER
, 1 );
47 $c->setopt( CURLOPT_NOBODY
, 0 );
49 $c->setopt( CURLOPT_VERBOSE
, 1 )
52 $c->setopt( CURLOPT_PROXY
, $config->{proxy
} )
55 $c->setopt( CURLOPT_PROXY
, "" )
56 if $config->{no_proxy
};
58 $c->setopt( CURLOPT_COOKIEJAR
, $config->{cookie_jar
} )
59 if $config->{cookie_jar
};
65 my ( $self, $no_socks_timeout ) = @_;
67 my $config = clive
::Config
->instance->config;
70 ->setopt( CURLOPT_CONNECTTIMEOUT
, $config->{connecttimeout
} || 30 );
73 ->setopt( CURLOPT_TIMEOUT
, $config->{connecttimeoutsocks
} || 30 )
74 unless $no_socks_timeout;
79 # Resetting SOCKS timeout is sufficient.
81 $self->{handle
}->setopt( CURLOPT_TIMEOUT
, 0 );
85 my ( $self, $url, $content, $what ) = @_;
87 my $log = clive
::Log
->instance;
89 my $_what = $what || $url;
90 $log->out("fetch $_what ...");
92 $self->{handle
}->setopt( CURLOPT_URL
, $url );
93 $self->{handle
}->setopt( CURLOPT_ENCODING
, "" );
96 open( my $fh, ">", $content );
98 $self->{handle
}->setopt( CURLOPT_WRITEDATA
, $fh );
101 my $rc = $self->{handle
}->perform;
107 my $httpcode = $self->{handle
}->getinfo(CURLINFO_RESPONSE_CODE
);
108 if ( $httpcode == 200 ) {
109 $log->out("done.\n");
113 $log->errn( CLIVE_NET
,
114 $self->{handle
}->strerror($httpcode) . " (http/$httpcode)" );
119 $log->errn( CLIVE_NET
,
120 $self->{handle
}->strerror($rc) . " (http/$rc)" );
124 from_to
( $$content, $1, "utf8" )
125 if ( $$content =~ /charset=(.*?)"/ );
127 from_to
( $$content, $1, "utf8" )
128 if ( $$content =~ /encoding="(.*?)"/ );
130 $$content = decode_utf8
( $$content, Encode
::FB_CROAK
);
135 sub queryFileLength
{
136 my ( $self, $props ) = @_;
138 my $log = clive
::Log
->instance;
139 $log->out("verify video link ...");
142 open( my $fh, ">", \
$buffer );
144 $self->{handle
}->setopt( CURLOPT_URL
, $$props->video_link );
145 $self->{handle
}->setopt( CURLOPT_WRITEDATA
, $fh );
148 $self->{handle
}->setopt( CURLOPT_NOBODY
, 1 );
151 my $rc = $self->{handle
}->perform;
156 $self->{handle
}->setopt( CURLOPT_HTTPGET
, 1 );
159 my $httpcode = $self->{handle
}->getinfo(CURLINFO_RESPONSE_CODE
);
160 if ( $httpcode == 200 || $httpcode == 206 ) {
161 $$props->file_length(
162 $self->{handle
}->getinfo(CURLINFO_CONTENT_LENGTH_DOWNLOAD
) );
165 = $self->{handle
}->getinfo(CURLINFO_CONTENT_TYPE
);
166 $$props->content_type($content_type);
168 # Figure out file suffix.
169 if ( $content_type =~ /\/(.*)/ ) {
170 my $suffix = $1; # Default to whatever was matched.
177 # Otherwise use "flv" for the above exceptions.
180 $suffix =~ tr
{x
-}//d; # e.g. x-flv -> flv.
181 $$props->file_suffix($suffix);
182 $log->out("done.\n");
186 $log->errn( CLIVE_NET
,
187 "$content_type: unexpected content-type" );
191 $log->errn( CLIVE_NET
,
192 $self->{handle
}->strerror($httpcode) . " (http/$httpcode)" );
196 $log->errn( CLIVE_NET
,
197 $self->{handle
}->strerror($rc) . " (http/$rc)" );
203 my ( $self, $props ) = @_;
205 my $log = clive
::Log
->instance;
206 my $config = clive
::Config
->instance->config;
207 my $initial = $$props->initial_length;
210 if ( $config->{continue} && $initial > 0 ) {
211 my $remaining = $$props->file_length - $initial;
214 "from: %d (%.1fM) remaining: %d (%.1fM)\n",
215 $initial, clive
::Util
::toMB
($initial),
216 $remaining, clive
::Util
::toMB
($remaining)
222 my $fpath = $$props->filename;
224 open( my $fh, $mode, $fpath )
225 or die("$fpath: $!");
227 $self->{handle
}->setopt( CURLOPT_URL
, $$props->video_link );
228 $self->{handle
}->setopt( CURLOPT_ENCODING
, "identity" );
229 $self->{handle
}->setopt( CURLOPT_WRITEDATA
, $fh );
230 $self->{handle
}->setopt( CURLOPT_HEADER
, 0 );
231 $self->{handle
}->setopt( CURLOPT_RESUME_FROM
, $$props->initial_length );
233 $self->{handle
}->setopt( CURLOPT_PROGRESSFUNCTION
, \
&progress_callback
);
234 $self->{handle
}->setopt( CURLOPT_NOPROGRESS
, 0 );
236 require clive
::Progress
::Bar
;
237 $bp = clive
::Progress
::Bar
->new($props);
239 if ( $config->{limit_rate
} ) {
240 $self->{handle
}->setopt( CURLOPT_MAX_RECV_SPEED_LARGE
,
241 $config->{limit_rate
} * 1024 );
244 setTimeout
( $self, 1 ); # 1=Do not enable SOCKS timeout.
245 my $rc = $self->{handle
}->perform;
250 $self->{handle
}->setopt( CURLOPT_MAX_RECV_SPEED_LARGE
, 0 );
251 $self->{handle
}->setopt( CURLOPT_HEADER
, 1 );
252 $self->{handle
}->setopt( CURLOPT_NOPROGRESS
, 1 );
253 $self->{handle
}->setopt( CURLOPT_RESUME_FROM
, 0 );
256 my $httpcode = $self->{handle
}->getinfo(CURLINFO_RESPONSE_CODE
);
257 if ( $httpcode == 200 || $httpcode == 206 ) {
261 $log->errn( CLIVE_NET
,
262 $self->{handle
}->strerror($httpcode) . " (http/$httpcode)" );
268 $rc == 42 ? CLIVE_STOP
: CLIVE_NET
,
269 $self->{handle
}->strerror($rc) . " (rc/$rc)"
274 clive
::Exec
->instance->resetStream;
280 sub progress_callback
{
281 my ( $percent, $stop_transfer, $props ) = $bp->update(@_);
282 clive
::Exec
->instance->runStream( $percent, $props );
283 return ($stop_transfer);
288 # I can't get no relief.