File Coverage

blib/lib/FlashVideo/Generic.pm
Criterion Covered Total %
statement 12 144 8.3
branch 0 80 0.0
condition 0 24 0.0
subroutine 4 9 44.4
pod 0 4 0.0
total 16 261 6.1


line stmt bran cond sub pod time code
1             # Part of get-flash-videos. See get_flash_videos for copyright.
2             package FlashVideo::Generic;
3              
4 1     1   10061 use strict;
  1         3  
  1         39  
5 1     1   6 use FlashVideo::Utils;
  1         24  
  1         136  
6 1     1   3135 use URI;
  1         5692  
  1         40  
7 1     1   7 use URI::Escape qw(uri_unescape);
  1         2  
  1         3210  
8              
9             my $video_re = qr!http[-:/a-z0-9%_.?=&]+@{[EXTENSIONS]}
10             # Grab any params that might be used for auth..
11             (?:\?[-:/a-z0-9%_.?=&]+)?!xi;
12              
13             sub find_video {
14 0     0 0   my ($self, $browser, $embed_url, $prefs) = @_;
15              
16             # First strategy - identify all the Flash video files, and download the
17             # biggest one. Yes, this is hacky.
18 0 0         if (!$browser->success) {
19 0           $browser->get($browser->response->header('Location'));
20 0 0         die "Couldn't download URL: " . $browser->response->status_line
21             unless $browser->success;
22             }
23              
24 0           my ($possible_filename, $actual_url, $title);
25 0           $title = extract_title($browser);
26              
27 0 0         my @flv_urls = map {
28 0           (m{http://.+?(http://.+?@{[EXTENSIONS]})}i) ? $1 : $_
  0            
29             } ($browser->content =~ m{($video_re)}gi);
30 0 0         if (@flv_urls) {
31 0           require LWP::Simple;
32 0           require Memoize;
33 0           Memoize::memoize("LWP::Simple::head");
34 0           @flv_urls = sort { (LWP::Simple::head($a))[1] <=> (LWP::Simple::head($b))[1] } @flv_urls;
  0            
35 0           $possible_filename = (split /\//, $flv_urls[-1])[-1];
36              
37             # Un-escape URLs if necessary
38 0 0         if ($flv_urls[-1] =~ /^http%3a%2f%2f/) {
39 0           $flv_urls[-1] = uri_unescape($flv_urls[-1])
40             }
41            
42 0           $actual_url = url_exists($browser->clone, $flv_urls[-1]);
43             }
44              
45 0           my $filename_is_reliable;
46              
47 0 0         if(!$actual_url) {
48 0           RE: for my $regex(
49             qr{(?si)]+)},
50             qr{(?si)]+)},
51 0           qr{(?si)]* href=["']?([^"'>]+?@{[EXTENSIONS]})},
52             qr{(?si)]*>.*?]*value=["']?([^"'>]+)},
53             qr{(?si)]*>(.*?)},
54             # Attempt to handle scripts using flashvars / swfobject
55             qr{(?si)]*>(.*?)}) {
56              
57 0           for my $param($browser->content =~ /$regex/gi) {
58 0           (my $url, $possible_filename, $filename_is_reliable) = find_file_param($browser->clone, $param, $prefs);
59              
60 0 0         if($url) {
61 0           my $resolved_url = url_exists($browser->clone, $url);
62 0 0         if($resolved_url) {
63 0           $actual_url = $resolved_url;
64 0           last RE;
65             }
66             }
67             }
68             }
69              
70 0 0         if(!$actual_url) {
71 0           for my $iframe($browser->content =~ /]+src=["']?([^"'>]+)/gi) {
72 0           $iframe = URI->new_abs($iframe, $browser->uri);
73 0           debug "Found iframe: $iframe";
74 0           my $sub_browser = $browser->clone;
75 0           $sub_browser->get($iframe);
76 0           ($actual_url) = eval { $self->find_video($sub_browser, undef, $prefs) };
  0            
77             }
78             }
79             }
80              
81 0           my @filenames;
82            
83 0 0         return $actual_url, $possible_filename if $filename_is_reliable;
84              
85 0           $possible_filename =~ s/\?.*//;
86             # The actual filename, provided it looks like it might be reasonable
87             # (not just numbers)..
88 0 0 0       push @filenames, $possible_filename if $possible_filename
89 0           && $possible_filename !~ /^[0-9_.]+@{[EXTENSIONS]}$/;
90              
91             # The title of the page, if it isn't similar to the filename..
92 0           my $ext = substr(($actual_url =~ /(@{[EXTENSIONS]})$/)[0], 1);
  0            
93 0 0 0       push @filenames, title_to_filename($title, $ext) if
94             $title && $title !~ /\Q$possible_filename\E/i;
95              
96             # A title with just the timestamp in it..
97 0 0         push @filenames, get_video_filename() if !@filenames;
98            
99 0 0         return $actual_url, @filenames if $actual_url;
100              
101             # As a last ditch attempt, download the SWF file as in some cases, sites
102             # use an SWF movie file for each FLV.
103              
104             # Get SWF URL(s)
105 0           my %swf_urls;
106              
107 0 0         if (eval { require URI::Find }) {
  0            
108             my $finder = URI::Find->new(
109 0 0   0     sub { $swf_urls{$_[1]}++ if $_[1] =~ /\.swf$/i }
110 0           );
111 0           $finder->find(\$browser->content);
112             }
113             else {
114             # Extract URLs in a frail way.
115 0           while ($browser->content =~ m{(http://[^ "']+?\.swf)}ig) {
116 0           $swf_urls{$_[1]}++;
117             }
118             }
119              
120 0 0         if (%swf_urls) {
121 0           foreach my $swf_url (keys %swf_urls) {
122 0 0         if (my ($flv_url, $title) = search_for_flv_in_swf($browser, $swf_url)) {
123 0           return $flv_url, title_to_filename($title);
124             }
125             }
126             }
127              
128 0           die "No URLs found";
129             }
130              
131             sub search_for_flv_in_swf {
132 0     0 0   my ($browser, $swf_url) = @_;
133              
134 0           $browser = $browser->clone();
135              
136 0           $browser->get($swf_url);
137              
138 0 0         if (!$browser->success) {
139 0           die "Couldn't download SWF URL $swf_url: " .
140             $browser->response->status_line();
141             }
142              
143             # SWF data might be compressed.
144 0           my $swf_data = $browser->content;
145              
146 0 0         if ('C' eq substr $swf_data, 0, 1) {
147 0 0         if (eval { require Compress::Zlib }) {
  0            
148 0           $swf_data = Compress::Zlib::uncompress(substr $swf_data, 8);
149             }
150             else {
151 0           die "Compress::Zlib is required to uncompress compressed SWF files.\n";
152             }
153             }
154              
155 0 0         if ($swf_data =~ m{(http://.{10,300}?\.flv)}i) {
156 0           my $flv_url = $1;
157              
158 0           my $filename = uri_unescape(File::Basename::basename(URI->new($flv_url)->path()));
159 0           $filename =~ s/\.flv$//i;
160              
161 0           return ($flv_url, $filename);
162             }
163              
164 0           return;
165             }
166              
167             sub find_file_param {
168 0     0 0   my($browser, $param, $prefs) = @_;
169              
170 0           for my $file($param =~ /(?:video|movie|file|path)_?(?:href|src|url)?['"]?\s*[=:,]\s*['"]?([^&'" ]+)/gi,
171             $param =~ /(?:config|playlist|options)['"]?\s*[,:=]\s*['"]?(http[^'"&]+)/gi,
172 0           $param =~ /['"=](.*?@{[EXTENSIONS]})/gi,
173 0           $param =~ /([^ ]+@{[EXTENSIONS]})/gi,
174             $param =~ /SWFObject\(["']([^"']+)/) {
175              
176 0           debug "Found $file";
177              
178 0           my ($actual_url, $filename, $filename_is_reliable) = guess_file($browser, $file, '', $prefs);
179              
180 0 0 0       if(!$actual_url && $file =~ /\?(.*)/) {
181             # Maybe we have query params?
182 0           debug "Trying query param on $1";
183              
184 0           for my $query_param(split /[;&]/, $1) {
185 0           my($query_key, $query_value) = split /=/, $query_param;
186 0           debug "Found $query_value from $query_key";
187              
188 0           ($actual_url, $filename, $filename_is_reliable)
189             = guess_file($browser, $query_value, '', $prefs);
190              
191 0 0         last if $actual_url;
192             }
193             }
194              
195 0 0         if($actual_url) {
196 0   0       my $possible_filename = $filename || (split /\//, $actual_url)[-1];
197              
198 0           return $actual_url, $possible_filename, $filename_is_reliable;
199             }
200             }
201              
202 0 0         if($param =~ m{(rtmp://[^ &"']+)}) {
203 0           info "This looks like RTMP ($1), no generic support yet..";
204             }
205            
206 0           return;
207             }
208              
209             sub guess_file {
210 0     0 0   my($browser, $file, $once, $prefs) = @_;
211              
212             # Contains lots of URI encoding, so try escaping..
213 0 0         $file = uri_unescape($file) if scalar(() = $file =~ /%[A-F0-9]{2}/gi) > 3;
214              
215 0           my $orig_uri = URI->new_abs($file, $browser->uri);
216              
217 0           info "Guessed $orig_uri trying...";
218              
219 0 0         if($orig_uri) {
220 0           my $uri = url_exists($browser->clone, $orig_uri);
221              
222 0 0         if($uri) {
    0          
223             # Check to see if this URL is for a supported site.
224 0           my ($package, $url) = FlashVideo::URLFinder->find_package($uri,
225             $browser->clone);
226              
227 0 0 0       if($package && $package ne __PACKAGE__) {
228 0           debug "$uri is supported by $package.";
229 0           (my $browser_on_supported_site = $browser->clone())->get($uri);
230 0           return $package->find_video($browser_on_supported_site, $uri, $prefs), 1;
231             }
232              
233 0           my $content_type = $browser->response->header("Content-type");
234              
235 0 0 0       if($content_type =~ m!^(text|application/xml)!) {
    0          
236             # Just in case someone serves the video itself as text/plain.
237 0           $browser->add_header("Range", "bytes=0-10000");
238 0           $browser->get($uri);
239 0           $browser->delete_header("Range");
240              
241 0 0 0       if(FlashVideo::Downloader->check_magic($browser->content)
242             || $uri =~ m!$video_re!) {
243             # It's a video..
244 0           debug "Found a video at $uri";
245 0           return $uri;
246             }
247              
248             # If this looks like HTML we have no hope of guessing right, so
249             # give up now.
250 0 0         return if $browser->content =~ /]*>/i;
251              
252 0 0 0       if($browser->content =~ m!($video_re)!) {
    0          
253             # Found a video URL
254 0           return $1;
255             } elsif(!defined $once
256             && $browser->content =~ m!(http[-:/a-zA-Z0-9%_.?=&]+)!i) {
257             # Try once more, one level deeper..
258 0           return guess_file($browser, $1, 1, $prefs);
259             } else {
260 0           info "Tried $uri, but no video URL found";
261             }
262             } elsif($content_type =~ m!application/! && $uri ne $orig_uri) {
263             # We were redirected, maybe something in the new URL?
264 0           return((find_file_param($browser, $uri))[0]);
265             } else {
266 0           return $uri->as_string;
267             }
268             } elsif(not defined $once) {
269             # Try using the location of the .swf file as the base, if it's different.
270 0 0         if($browser->content =~ /["']([^ ]+\.swf)/) {
271 0           my $swf_uri = URI->new_abs($1, $browser->uri);
272 0 0         if($swf_uri) {
273 0           my $new_uri = URI->new_abs($file, $swf_uri);
274 0           debug "Found SWF: $swf_uri -> $new_uri";
275 0 0         if($new_uri ne $uri) {
276 0           return guess_file($browser, $new_uri, 1, $prefs);
277             }
278             }
279             }
280             }
281             }
282              
283 0           return;
284             }
285              
286             1;