/usr/share/perl5/FlashVideo/RTMPDownloader.pm is in get-flash-videos 1.25~git2012.06.27-2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | # Part of get-flash-videos. See get_flash_videos for copyright.
package FlashVideo::RTMPDownloader;
use strict;
use base 'FlashVideo::Downloader';
use IPC::Open3;
use Fcntl ();
use Symbol qw(gensym);
use FlashVideo::Utils;
use constant LATEST_RTMPDUMP => 2.2;
sub download {
my ($self, $rtmp_data, $file) = @_;
$self->{printable_filename} = $file;
$file = $rtmp_data->{flv} = $self->get_filename($file);
if (-s $file && !$rtmp_data->{live}) {
info "RTMP output filename '$self->{printable_filename}' already " .
"exists, asking to resume...";
$rtmp_data->{resume} = '';
}
if(my $socks = FlashVideo::Mechanize->new->get_socks_proxy) {
$rtmp_data->{socks} = $socks;
}
my($r_fh, $w_fh); # So Perl doesn't close them behind our back..
if ($rtmp_data->{live} && $self->action eq 'play') {
# Playing live stream, we pipe this straight to the player, rather than
# saving on disk.
# XXX: The use of /dev/fd could go away now rtmpdump supports streaming to
# STDOUT.
pipe($r_fh, $w_fh);
my $pid = fork;
die "Fork failed" unless defined $pid;
if(!$pid) {
fcntl $r_fh, Fcntl::F_SETFD(), ~Fcntl::FD_CLOEXEC();
exec $self->replace_filename($self->player, "/dev/fd/" . fileno $r_fh);
die "Exec failed\n";
}
fcntl $w_fh, Fcntl::F_SETFD(), ~Fcntl::FD_CLOEXEC();
$rtmp_data->{flv} = "/dev/fd/" . fileno $w_fh;
$self->{stream} = undef;
}
my $prog = $self->get_rtmp_program;
if($prog eq 'flvstreamer' && ($rtmp_data->{rtmp} =~ /^rtmpe:/ || $rtmp_data->{swfhash})) {
error "FLVStreamer does not support "
. ($rtmp_data->{swfhash} ? "SWF hashing" : "RTMPE streams")
. ", please install rtmpdump.";
exit 1;
}
if($self->debug) {
$rtmp_data->{verbose} = undef;
}
my($return, @errors) = $self->run($prog, $rtmp_data);
if($return != 0 && "@errors" =~ /failed to connect/i) {
# Try port 443 as an alternative
info "Couldn't connect on RTMP port, trying port 443 instead";
$rtmp_data->{port} = 443;
($return, @errors) = $self->run($prog, $rtmp_data);
}
if($file ne '-' && (-s $file < 100 || !$self->check_file($file))) {
# This avoids trying to resume an invalid file
error "Download failed, no valid file downloaded";
unlink $rtmp_data->{flv};
return 0;
}
if($return == 2) {
info "\nDownload incomplete -- try running again to resume.";
return 0;
} elsif($return) {
info "\nDownload failed.";
return 0;
}
return -s $file;
}
sub get_rtmp_program {
if(is_program_on_path("rtmpdump")) {
return "rtmpdump";
} elsif(is_program_on_path("flvstreamer")) {
return "flvstreamer";
}
# Default to rtmpdump
return "rtmpdump";
}
sub get_command {
my($self, $rtmp_data, $debug) = @_;
return map {
my $arg = $_;
(ref $rtmp_data->{$arg} eq 'ARRAY'
# Arrayref means multiple options of the same type
? (map {
("--$arg" => $debug
? $self->shell_escape($_)
: $_) } @{$rtmp_data->{$arg}})
# Single argument
: ("--$arg" => (($debug && $rtmp_data->{$arg})
? $self->shell_escape($rtmp_data->{$arg})
: $rtmp_data->{$arg}) || ()))
} keys %$rtmp_data;
}
sub run {
my($self, $prog, $rtmp_data) = @_;
debug "Running $prog", join(" ", $self->get_command($rtmp_data, 1));
my($in, $out, $err);
$err = gensym;
my $pid = open3($in, $out, $err, $prog, $self->get_command($rtmp_data));
# Windows doesn't send signals to child processes, so we need to do it
# manually to ensure that we don't have stray rtmpdump processes.
local $SIG{INT};
if ($^O =~ /mswin/i) {
$SIG{INT} = sub {
kill 'TERM', $pid;
exit;
};
}
my $complete = 0;
my $buf = "";
my @error;
while(sysread($err, $buf, 128, length $buf) > 0) {
$buf =~ s/\015\012/\012/g;
my @parts = split /\015/, $buf;
$buf = "";
for(@parts) {
# Hide almost everything from rtmpdump, it's less confusing this way.
if(/^((?:DEBUG:|WARNING:|Closing connection|ERROR: No playpath found).*)\n/) {
debug "$prog: $1";
} elsif(/^(ERROR: .*)\012/) {
push @error, $1;
info "$prog: $1";
} elsif(/^([0-9.]+) kB(?:\s+\/ \S+ sec)?(?: \(([0-9.]+)%\))?/i) {
$self->{downloaded} = $1 * 1024;
my $percent = $2;
if($self->{downloaded} && $percent != 0) {
# An approximation, but should be reasonable if we don't have the size.
$self->{content_length} = $self->{downloaded} / ($percent / 100);
}
$self->progress;
} elsif(/\012$/) {
for my $l(split /\012/) {
if($l =~ /^[A-F0-9]{,2}(?:\s+[A-F0-9]{2})*\s*$/) {
debug $l;
} elsif($l =~ /Download complete/) {
$complete = 1;
} elsif($l =~ /\s+filesize\s+(\d+)/) {
$self->{content_length} = $1;
} elsif($l =~ /\w/) {
print STDERR "\r" if $self->{downloaded};
info $l;
if($l =~ /^RTMPDump v([0-9.]+)/ && $1 < LATEST_RTMPDUMP) {
error "==== Using the latest version of RTMPDump (version "
. LATEST_RTMPDUMP . ") is recommended. ====";
}
}
}
if(/open3/) {
error "\nMake sure you have 'rtmpdump' or 'flvstreamer' installed and available on your PATH.";
return 0;
}
} else {
# Hack; assume lack of newline means it was an incomplete read..
$buf = $_;
}
}
# Should be about enough..
if(defined $self->{stream} && $self->{downloaded} > 300_000) {
$self->{stream}->();
}
}
waitpid $pid, 0;
return $? >> 8, @error;
}
1;
|