File Coverage

blib/lib/WWW/YouTube/Info/Simple.pm
Criterion Covered Total %
statement 76 107 71.0
branch 22 56 39.2
condition 2 12 16.6
subroutine 12 14 85.7
pod 5 5 100.0
total 117 194 60.3


line stmt bran cond sub pod time code
1             package WWW::YouTube::Info::Simple;
2              
3 8     8   531202 use 5.008;
  8         108  
4 8     8   65 use strict;
  8         16  
  8         217  
5 8     8   41 use warnings;
  8         15  
  8         726  
6              
7             require Exporter;
8             require WWW::YouTube::Info;
9              
10             our @ISA = qw(
11             Exporter
12             WWW::YouTube::Info
13             );
14              
15             our @EXPORT = qw(
16             );
17              
18             our $VERSION = '0.14';
19              
20 8     8   54 use Carp;
  8         22  
  8         549  
21 8     8   5014 use Data::Dumper;
  8         54039  
  8         10509  
22              
23             =head1 NAME
24              
25             WWW::YouTube::Info::Simple - simple interface to WWW::YouTube::Info
26              
27             =head1 SYNOPSIS
28              
29             =head2 Perhaps a little code snippet?
30              
31             #!/usr/bin/perl
32            
33             use strict;
34             use warnings;
35            
36             use WWW::YouTube::Info::Simple;
37            
38             # id taken from YouTube video URL
39             my $id = 'foobar';
40            
41             my $yt = WWW::YouTube::Info::Simple->new($id);
42            
43             # hash reference holds values gained via https://youtube.com/get_video_info?video_id=foobar
44             my $info = $yt->get_info();
45             # this is basically an inheritance to get_info() at WWW::YouTube::Info
46             # $info->{title} # e.g.: Foo+bar+-+%27Foobar%27
47             # $info->{author} # e.g.: foobar
48             # $info->{keywords} # e.g.: Foo%2Cbar%2CFoobar
49             # $info->{length_seconds} # e.g.: 60
50             # $info->{fmt_map} # e.g.: 22%2F1280x720%2F9%2F0%2F115%2C35%2F854x480%2F9%2F0%2F115%2C34%2F640x360%2F9%2 ..
51             # $info->{fmt_url_map} # e.g.: 22%7Chttps%3A%2F%2Fv14.lscache1.c.youtube.com%2Fvideoplayback%3Fip%3D131.0.0. ..
52             # $info->{fmt_stream_map} # e.g.: 22%7Chttps%3A%2F%2Fv14.lscache1.c.youtube.com%2Fvideoplayback%3Fip%3D131.0.0. ..
53            
54             # array reference holds values keywords
55             my $keys = $yt->get_keywords();
56             # $keys->[0] # e.g.: Foo
57             # $keys->[1] # e.g.: bar
58             # ..
59            
60             # hash reference holds values quality -> resolution
61             my $res = $yt->get_resolution();
62             # $res->{35} # e.g.: 854x480
63             # $res->{22} # e.g.: 1280x720
64             # ..
65            
66             # URL and masquerading decoded title
67             my $title = $yt->get_title(); # e.g.: Foo bar - 'Foobar'
68            
69             # hash reference holds values quality -> url
70             my $url = $yt->get_url();
71             # $url->{35} e.g.: https://v14.lscache1.c.youtube.com/videoplayback?ip=131.0.0.0 ..
72             # $url->{22} e.g.: https://v14.lscache1.c.youtube.com/videoplayback?ip=131.0.0.0 ..
73             # ..
74            
75             # URL decoded RTMPE URL
76             my $conn = $yt->get_conn(); # e.g.: rtmpe://cp59009.edgefcs.net/youtube?auth=daEcaboc8dvawbcbxazdobDcZajcDdgcfae ..
77            
78             # Remark:
79             # You might want to check $info->{status} before further workout,
80             # as some videos have copyright issues indicated, for instance, by
81             # $info->{status} ne 'ok'.
82              
83             =head1 DESCRIPTION
84              
85             I guess its pretty much self-explanatory ..
86              
87             =head1 METHODS
88              
89             =cut
90              
91             =head2 get_keywords
92              
93             Returns undef if status ne 'ok'.
94             Croaks if not available.
95              
96             =cut
97              
98             sub get_keywords {
99 0     0 1 0 my ($self) = @_;
100              
101 0 0       0 $self->get_info() unless exists($self->{info});
102 0 0       0 return if ( $self->{info}->{status} ne 'ok' );
103              
104 0         0 my $keywords = $self->{info}->{'keywords'};
105 0 0       0 croak "no keywords found!" unless $keywords;
106              
107 0         0 my @keywords_parts = split /%2C/, $keywords;
108 0         0 foreach my $item ( @keywords_parts ) {
109 0 0       0 next unless $item;
110 0         0 $item = _url_decode($item);
111 0         0 push @{$self->{keywords}}, $item;
  0         0  
112             }
113              
114 0         0 return $self->{keywords};
115             }
116              
117             =head2 get_resolution
118              
119             Returns undef if status ne 'ok'.
120             Croaks if not available.
121              
122             =cut
123              
124             sub get_resolution {
125 2     2 1 636885 my ($self) = @_;
126              
127 2 50       8 $self->get_info() unless exists($self->{info});
128 2 50       8 return if ( $self->{info}->{status} ne 'ok' );
129              
130             # quality and resolution
131 2         26 my $fmt_map = $self->{info}->{'fmt_map'};
132 2 50       7 unless ( $fmt_map ) {
133             # fallback to fmt_list
134             # as fmt_map doesnt't seem to be supported any more
135 2 50       6 croak "no resolutions found!" unless $self->_fmt_list();
136             }
137             else {
138             # process fmt_map
139 0         0 my @fmt_map_parts = split /%2F9%2F0%2F115%2C/, $fmt_map;
140 0         0 foreach my $item ( @fmt_map_parts ) {
141 0         0 my ($quality, $resolution) = split /%2F/, $item;
142 0 0 0     0 next unless $quality and $resolution;
143 0         0 $self->{resolution}->{$quality} = $resolution;
144             }
145             }
146              
147 2         15 return $self->{resolution};
148             }
149              
150             =head2 get_title
151              
152             Returns undef if status ne 'ok'.
153             Defaults to _id if not available.
154              
155             =cut
156              
157             sub get_title {
158 1     1 1 636278 my ($self) = @_;
159              
160 1 50       6 $self->get_info() unless exists($self->{info});
161 1 50       8 return if ( $self->{info}->{status} ne 'ok' );
162              
163 1         22 my $title = $self->{info}->{'title'};
164              
165 1 50       4 unless ( $title ) {
166             # fallback to _id
167 1         261 carp "no title found! fallback to VIDEO_ID";
168 1         95 $title = $self->{_id};
169             }
170             else {
171             # process title
172 0         0 $title = _url_decode($title);
173 0         0 $title =~ s/\+/ /g;
174             }
175              
176 1         6 $self->{title} = $title;
177              
178 1         13 return $self->{title};
179             }
180              
181             =head2 get_url
182              
183             Returns undef if status ne 'ok'.
184             Croaks if not available.
185              
186             use WWW::YouTube::Info::Simple;
187            
188             # id taken from YouTube video URL
189             my $id = 'foobar';
190            
191             my $yt = WWW::YouTube::Info::Simple->new($id);
192            
193             # hash reference holds values quality -> url
194             my $url = $yt->get_url();
195             # $url->{35} e.g.: https://v14.lscache1.c.youtube.com/videoplayback?ip=131.0.0.0 ..
196             # $url->{22} e.g.: https://v14.lscache1.c.youtube.com/videoplayback?ip=131.0.0.0 ..
197             # ..
198              
199             YouTube videos can be downloaded in given qualities by means of these URLs and the usual suspects (C, ..).
200              
201             =cut
202              
203             sub get_url {
204 2     2 1 634209 my ($self) = @_;
205              
206 2 50       9 $self->get_info() unless exists($self->{info});
207 2 50       9 return if ( $self->{info}->{status} ne 'ok' );
208              
209             # quality and URL
210 2         23 my $fmt_url_map = $self->{info}->{'fmt_url_map'};
211 2 50       8 unless ( $fmt_url_map ) {
212             # fallback to url_encoded_fmt_stream_map
213             # as fmt_url_map doesnt't seem to be supported any more
214 2 50       8 croak "no URLs found!" unless $self->_url_encoded_fmt_stream_map();
215             }
216             else {
217             # process fmt_url_map
218 0         0 my @fmt_url_map_parts = split /%2C/, $fmt_url_map;
219 0         0 foreach my $item ( @fmt_url_map_parts ) {
220 0         0 my ($quality, $url) = split /%7C/, $item;
221 0 0 0     0 next unless $quality and $url;
222 0         0 $url = _url_decode($url);
223 0         0 $self->{url}->{$quality} = $url;
224             }
225             }
226              
227 2         15 return $self->{url};
228             }
229              
230             =head2 get_conn
231              
232             Returns undef if status ne 'ok'.
233             Croaks if not available.
234              
235             use WWW::YouTube::Info::Simple;
236            
237             # id taken from YouTube video URL
238             my $id = 'foobar';
239            
240             my $yt = WWW::YouTube::Info::Simple->new($id);
241            
242             # URL decoded RTMPE URL
243             my $conn = $yt->get_conn(); # e.g.: rtmpe://cp59009.edgefcs.net/youtube?auth=daEcaboc8dvawbcbxazdobDcZajcDdgcfae ..
244              
245             A YouTube RTMPE stream can be accessed via this URL and downloaded by
246             means of the usual suspects (C, ..).
247             The URL looses its validity after approx. 30 seconds (experimental value).
248             Gathering a fresh RTMPE URL regarding the same VIDEO_ID and the
249             C capability might circumvent this inconvenience.
250              
251             =cut
252              
253             sub get_conn {
254 1     1 1 639109 my ($self) = @_;
255              
256 1 50       8 $self->get_info() unless exists($self->{info});
257 1 50       6 return if ( $self->{info}->{status} ne 'ok' );
258              
259 1         20 my $conn = $self->{info}->{'conn'};
260 1 50       250 croak "no conn found!" unless $conn;
261              
262 0         0 $self->{conn} = _url_decode($conn);
263              
264 0         0 return $self->{conn};
265             }
266              
267              
268             sub _url_encoded_fmt_stream_map {
269 2     2   4 my ($self) = @_;
270              
271 2 50       6 $self->get_info() unless exists($self->{info});
272 2 50       7 return if ( $self->{info}->{status} ne 'ok' );
273              
274             # quality and URL
275 2         5 my $url_encoded_fmt_stream_map = $self->{info}->{'url_encoded_fmt_stream_map'};
276 2 50       5 return unless $url_encoded_fmt_stream_map;
277              
278 2         17 my @url_encoded_fmt_stream_map_parts = split /%2C/, $url_encoded_fmt_stream_map;
279 2         6 foreach my $item ( @url_encoded_fmt_stream_map_parts ) {
280 4         10 $item = _url_decode($item);
281 4         52 (my $url = $item) =~ s/.*url=(.*)(?:&.*|$)/$1/;
282 4         10 $url = _url_decode($url);
283 4         23 (my $quality = $url) =~ s/.*&itag=(\d+)&.*/$1/;
284              
285             # fix multiple occurrences of parameters
286 4         18 my ($url_base, $params) = split /\?/, $url;
287 4         41 my %params_uniq = map { $_ => 1 } split /&/, $params;
  112         228  
288 4         41 my $params_uniq = join('&', keys %params_uniq);
289 4         17 $url = $url_base . '?' . $params_uniq;
290              
291 4 50 33     21 next unless $quality and $url;
292 4         26 $self->{url}->{$quality} = $url;
293             }
294              
295 2         9 return $self->{url};
296             }
297              
298             sub _fmt_list {
299 2     2   4 my ($self) = @_;
300              
301 2 50       6 $self->get_info() unless exists($self->{info});
302 2 50       6 return if ( $self->{info}->{status} ne 'ok' );
303              
304             # quality and resolution
305 2         6 my $fmt_list = $self->{info}->{'fmt_list'};
306 2 50       6 return unless $fmt_list;
307              
308 2         7 my @fmt_list_parts = split /%2C/, $fmt_list;
309 2         6 foreach my $item ( @fmt_list_parts ) {
310 4         14 my ($quality, $resolution, @rest) = split /%2F/, $item;
311 4 50 33     20 next unless $quality and $resolution;
312 4         13 $self->{resolution}->{$quality} = $resolution;
313             }
314              
315 2         12 return $self->{resolution};
316             }
317              
318             sub _url_encode {
319 0     0   0 my $string = shift;
320              
321             # URLencode
322 0         0 $string =~ s/([^A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg;
  0         0  
323              
324 0         0 return $string;
325             }
326              
327             sub _url_decode {
328 8     8   14 my $string = shift;
329              
330             # URLdecode
331 8         33 $string =~ s/\%([A-Fa-f0-9]{2})/pack('C', hex($1))/seg;
  732         2146  
332              
333 8         24 return $string;
334             }
335              
336             1;
337              
338             __END__