File Coverage

blib/lib/WebService/GData/YouTube/Feed/Video.pm
Criterion Covered Total %
statement 146 240 60.8
branch 36 88 40.9
condition 7 15 46.6
subroutine 41 55 74.5
pod 29 34 85.2
total 259 432 59.9


line stmt bran cond sub pod time code
1             package WebService::GData::YouTube::Feed::Video;
2              
3 5     5   59909 use WebService::GData;
  5         11  
  5         29  
4 5     5   28 use base 'WebService::GData::Feed::Entry';
  5         6  
  5         2221  
5 5     5   30 use WebService::GData::Constants qw(:all);
  5         10  
  5         2299  
6 5     5   2018 use WebService::GData::YouTube::Constants qw(:all);
  5         9  
  5         1208  
7 5     5   1770 use WebService::GData::YouTube::StagingServer ();
  5         15  
  5         110  
8 5     5   1680 use WebService::GData::Error;
  5         14  
  5         58  
9 5     5   3012 use WebService::GData::Node::PointEntity();
  5         14  
  5         112  
10 5     5   2850 use WebService::GData::YouTube::YT::GroupEntity();
  5         21  
  5         136  
11 5     5   3450 use WebService::GData::YouTube::YT::AccessControl();
  5         15  
  5         154  
12 5     5   2928 use WebService::GData::YouTube::YT::Rating();
  5         13  
  5         99  
13 5     5   2978 use WebService::GData::YouTube::YT::Recorded();
  5         16  
  5         99  
14 5     5   32 use WebService::GData::Node::Media::Category();
  5         8  
  5         83  
15 5     5   27 use WebService::GData::Collection;
  5         10  
  5         75  
16              
17             our $VERSION = 0.02_01;
18             our $ROOT_URI = BASE_URI;
19             our $UPLOAD_BASE_URI = UPLOAD_BASE_URI . PROJECTION . '/users/default/uploads/';
20             our $BASE_URI = BASE_URI . PROJECTION . '/users/default/uploads/';
21             our $API_DOMAIN_URI = API_DOMAIN_URI;
22              
23             if ( WebService::GData::YouTube::StagingServer->is_on ) {
24             $ROOT_URI = STAGING_BASE_URI;
25             $UPLOAD_BASE_URI =
26             STAGING_UPLOAD_BASE_URI . PROJECTION . '/users/default/uploads/';
27             $BASE_URI = STAGING_BASE_URI . PROJECTION . '/users/default/uploads/';
28             $API_DOMAIN_URI = STAGING_API_DOMAIN_URI;
29              
30             }
31              
32             use constant {
33 5         17009 DIRECT_UPLOAD => 'DIRECT_UPLOAD',
34             BROWSER_UPLOAD => 'BROWSER_UPLOAD'
35 5     5   42 };
  5         12  
36              
37             sub __init {
38 2     2   6 my ( $this, $feed, $req ) = @_;
39              
40 2   50     10 $feed ||= {};
41 2 50       12 if ( ref($feed) ne 'HASH' ) {
42 0         0 $req = $feed;
43 0         0 $feed = {};
44             }
45              
46 2         80 $this->SUPER::__init( $feed, $req );
47 2   100     47 $this->_media(
48             new WebService::GData::YouTube::YT::GroupEntity(
49             $feed->{'media$group'} || {}
50             )
51             );
52 2         33 $this->{_rating} =
53             new WebService::GData::YouTube::YT::Rating( $feed->{'yt$rating'} );
54 2         34 $this->{_recorded} =
55             new WebService::GData::YouTube::YT::Recorded( $feed->{'yt$recorded'} );
56 2         14 $this->_entity->child( $this->_media )->child( $this->{_rating} )
57             ->child( $this->{_recorded} );
58             }
59              
60             sub next_url {
61 0     0 0 0 my $this = shift;
62 0 0       0 if ( @_ == 1 ) {
63 0         0 $this->{next_url} = _urlencode( shift() );
64             }
65 0         0 return $this->{next_url};
66             }
67              
68             sub location {
69 1     1 1 3 my ( $this, $pos ) = @_;
70 1         4 my $where = $this->{_feed}->{'georss$where'};
71 1 50 33     11 if ( ref($where) eq 'HASH' && !$this->{_where} ) {
72 1         18 $this->_location(
73             new WebService::GData::Node::PointEntity(
74             $where->{'gml$Point'}->{'gml$pos'}
75             )
76             );
77             }
78             else {
79 0         0 $this->{_feed}->{'georss$where'} = {};
80 0         0 $this->_location( new WebService::GData::Node::PointEntity() );
81             }
82 1 50       5 if ($pos) {
83 0         0 $this->_location->pos($pos);
84             }
85 1 50       3 return ref $this->_location->pos ? '' : $this->_location->pos;
86              
87             }
88              
89             sub _location {
90 3     3   13 my ( $this, $instance ) = @_;
91 3 100       8 if ($instance) {
92 1         4 $this->{_where} = $instance;
93 1         10 $this->_entity->child($instance);
94             }
95 3         24 $this->{_where};
96             }
97              
98             sub view_count {
99 1     1 1 3 my $this = shift;
100 1         8 $this->{_feed}->{'yt$statistics'}->{'viewCount'};
101             }
102              
103             sub favorite_count {
104 1     1 1 3 my $this = shift;
105 1         8 $this->{_feed}->{'yt$statistics'}->{'favoriteCount'};
106             }
107              
108             sub _media {
109 39     39   73 my ( $this, $instance ) = @_;
110 39 100       147 if ($instance) {
111 2         6 $this->{_media} = $instance;
112             }
113 39         408 $this->{_media};
114             }
115              
116             sub media_player {
117 1     1 1 3 my $this = shift;
118 1         5 $this->_media->player( {} )->url;
119             }
120              
121             sub restriction {
122 3     3 1 7 my $this = shift;
123 3         12 $this->_media->restriction;
124             }
125              
126             sub denied_countries {
127 1     1 1 3 my $this = shift;
128 1         4 my $denied = $this->_media->restriction->relationship('deny');
129 1 50       8 return if !ref $denied;
130 1         2 my @countries;
131 1         4 foreach my $d (@$denied) {
132 1 50       25 push @countries, $d->text if ( $d->type eq 'country' );
133             }
134 1         12 return join ' ', @countries;
135             }
136              
137             sub aspect_ratio {
138 1     1 1 4 my $this = shift;
139 1         4 $this->_media->aspect_ratio;
140             }
141              
142             sub video_id {
143 3     3 1 15 my ( $this, $id ) = @_;
144 3 100       12 $this->_media->videoid($id) if $id;
145 3 50       21 ref $this->_media->videoid ? '' : $this->_media->videoid;
146             }
147              
148             sub duration {
149 1     1 1 3 my $this = shift;
150 1         4 $this->_media->duration( {} )->seconds;
151             }
152              
153             sub content {
154 3     3 1 5 my $this = shift;
155 3         9 $this->_media->content;
156             }
157              
158             sub thumbnails {
159 2     2 1 5 my $this = shift;
160 2         5 $this->_media->thumbnail;
161             }
162              
163             sub uploaded {
164 1     1 1 4 my $this = shift;
165 1         4 $this->_media->uploaded;
166             }
167              
168             sub uploader {
169 1     1 1 3 my $this = shift;
170 1 50       2 if ( @{ $this->_media->credit->role('uploader') } > 0 ) {
  1         4  
171 1         4 $this->_media->credit->role('uploader')->[0]->text;
172             }
173             }
174              
175             sub category {
176 2     2 1 3 my $this = shift;
177              
178 2 50       6 if ( @_ == 1 ) {
179 0 0       0 if ( !$this->_media->category->isa('WebService::GData::Collection') ) {
180 0         0 $this->_media->swap( $this->_media->category,
181             new WebService::GData::Collection() );
182             }
183 0         0 push @{ $this->_media->category },
  0         0  
184             new WebService::GData::Node::Media::Category(
185             {
186             '$t' => $_[0],
187             'label' => $_[0],
188             'scheme' =>
189             'http://gdata.youtube.com/schemas/2007/categories.cat'
190             }
191             );
192             }
193 2         6 $this->_media->category;
194             }
195              
196             sub genre {
197 1     1 1 3 my $this = shift;
198 1 50       2 if ( @{ $this->_media->category } > 0 ) {
  1         3  
199 1         4 $this->_media->category->[0]->text;
200             }
201             }
202              
203             sub is_read_only {
204 1     1 1 2 my $this = shift;
205 1 50       3 return 1 if ( @{ $this->links->rel('edit') } > 0 );
  1         4  
206             }
207              
208             sub description {
209 1     1 1 3 my $this = shift;
210 1 50       5 if ( @_ == 1 ) {
211 0         0 $this->_media->description( $_[0] );
212 0         0 $this->_media->description( {} )->type("plain");
213             }
214 1 50       4 $this->_media->description || '';
215             }
216              
217             sub title {
218 3     3 1 45 my $this = shift;
219 3 100       12 if ( @_ == 1 ) {
220 1         7 $this->_media->title( $_[0] );
221              
222 1         3 $this->_media->title( {} )->type("plain");
223             }
224 3 50       10 $this->_media->title || '';
225             }
226              
227             sub keywords {
228 3     3 1 6 my $this = shift;
229 3 100       10 if ( @_ >= 1 ) {
230 1         6 return $this->_media->keywords( join( ',', @_ ) );
231             }
232 2 50       5 $this->_media->keywords || '';
233             }
234              
235             sub is_private {
236 0     0 1 0 my $this = shift;
237 0 0       0 if ( @_ == 1 ) {
238 0         0 $this->_media->{'_private'} =
239             new WebService::GData::YouTube::YT::Private();
240 0         0 $this->_media->_entity->child( $this->_media->{'_private'} );
241             }
242 0 0       0 return ( $this->_media->private ) ? 1 : 0;
243             }
244              
245             sub comments {
246 1     1 1 3 my $this = shift;
247 1         11 $this->{_feed}->{'gd$comments'}->{'gd$feedLink'}->{'href'};
248             }
249              
250             sub appcontrol_state {
251 0     0 1 0 my $this = shift;
252 0         0 return $this->{_feed}->{'app$control'}->{'yt$state'}->{reasonCode};
253             }
254              
255             #####WRITE FUNCTIONS########################
256              
257             sub _access_control {
258 18     18   33 my ( $this, $instance ) = @_;
259 18 100       44 if ($instance) {
260 1         3 $this->{_access_control} = $instance;
261 1         6 $this->_entity->child($instance);
262             }
263 18         91 $this->{_access_control};
264             }
265              
266             sub access_control {
267 3     3 1 7 my $this = shift;
268 3 100       10 if ( !$this->_access_control ) {
269 1         8 $this->_access_control( new WebService::GData::Collection() );
270 1   50     6 my $access = $this->{_feed}->{'yt$accessControl'} || [];
271 1         3 foreach my $control (@$access) {
272 7         13 push @{ $this->_access_control },
  7         12  
273             new WebService::GData::YouTube::YT::AccessControl($control);
274             }
275             }
276 3 100       216 if ( @_ == 2 ) {
277              
278             #first check if the action is already set and if so update
279 1         5 my $ret = $this->_access_control->action( $_[0] );
280 1 50       6 if ( @$ret > 0 ) {
281 1         6 $ret->[0]->permission( $_[1] );
282             }
283              
284             #if not, just push a new entry;
285             else {
286 0         0 push @{ $this->_access_control },
  0         0  
287             new WebService::GData::YouTube::YT::AccessControl(
288             { action => $_[0], permission => $_[1] } );
289             }
290             }
291 3 100       12 if ( @_ == 1 ) {
292              
293 1         2 return $this->_access_control->action( $_[0] )->[0];
294              
295             }
296 2         6 $this->_access_control;
297             }
298              
299             sub delete {
300 0     0 1 0 my $this = shift;
301              
302 0         0 my $uri =
303 0 0       0 @{ $this->links->rel('edit') } > 0
304             ? $this->links->rel('edit')->[0]->href
305             : $BASE_URI . $this->video_id;
306 0         0 $this->{_request}->delete( $uri, 0 );
307             }
308              
309             sub rate {
310 0     0 1 0 my ( $this, $val ) = @_;
311 0 0       0 $this->rating->value($val) if $val;
312 0         0 my $uri =
313             $ROOT_URI . PROJECTION . '/videos/' . $this->video_id . '/ratings';
314 0         0 $this->{_request}->insert( $uri, XML_HEADER . $this->serialize() );
315             }
316              
317             sub add_video_response {
318 0     0 1 0 my ( $this, $response ) = @_;
319 0         0 my $uri =
320             $ROOT_URI . PROJECTION . '/videos/' . $this->video_id . '/responses';
321 0         0 $this->{_request}->insert( $uri, XML_HEADER . $response->serialize() );
322             }
323              
324             sub delete_video_response {
325 0     0 1 0 my ( $this, $response_id ) = @_;
326 0         0 my $uri =
327             $ROOT_URI. PROJECTION
328             . '/videos/'. $this->video_id
329             . '/responses/' . $response_id;
330 0         0 $this->{_request}->delete($uri);
331             }
332              
333             sub add_favorite_video {
334 0     0 1 0 my ( $this,$id) = @_;
335            
336 0 0       0 $this->id($id) if $id;
337            
338 0 0       0 if($this->id){
339 0         0 my $uri =
340             $ROOT_URI . PROJECTION . '/users/default/favorites';
341 0         0 $this->{_request}->insert( $uri, XML_HEADER . $this->serialize() );
342             }
343             }
344              
345             sub save {
346 0     0 1 0 my ($this) = @_;
347 0         0 my $content = XML_HEADER . $this->serialize();
348              
349 0 0       0 if ( $this->video_id ) {
350              
351 0         0 my $ret =
352             $this->{_request}->update( $BASE_URI . $this->video_id, $content );
353             }
354             else {
355              
356 0 0       0 if ( $this->upload_mode eq DIRECT_UPLOAD ) {
357 0         0 $this->direct_uploading( $UPLOAD_BASE_URI, $content );
358             }
359             else {
360 0         0 return $this->browser_uploading( $UPLOAD_BASE_URI, $content );
361             }
362             }
363             }
364              
365             #video upload
366              
367             sub filename {
368 0     0 0 0 my $this = shift;
369 0 0       0 return $this->{_filename} = $_[0] if ( @_ == 1 );
370 0         0 $this->{_filename};
371             }
372              
373             #TODO: stream
374             sub _binary_data {
375 0     0   0 my $this = shift;
376              
377 0 0       0 if ( @_ == 1 ) {
378 0         0 my $fh = $_[0];
379 0         0 binmode($fh);
380 0         0 my $data = '';
381 0         0 while ( read $fh, my $buf, 1024 ) {
382 0         0 $data .= $buf;
383             }
384 0         0 close $fh;
385 0         0 return $this->{_binary_data} = $data;
386             }
387 0         0 $this->{_binary_data};
388             }
389              
390             sub upload_mode {
391 0     0 0 0 my $this = shift;
392 0 0       0 if ( @_ == 1 ) {
393 0         0 $this->{_UPLOAD_MODE} = shift;
394 0 0 0     0 $this->{_UPLOAD_MODE} = undef
395             if ( $this->{_UPLOAD_MODE} ne DIRECT_UPLOAD
396             || $this->{_UPLOAD_MODE} ne BROWSER_UPLOAD );
397             }
398 0 0       0 $this->{_UPLOAD_MODE} = BROWSER_UPLOAD if ( !$this->{_UPLOAD_MODE} );
399 0         0 $this->{_UPLOAD_MODE};
400             }
401              
402             sub browser_uploading {
403 0     0 0 0 my ( $this, $uri, $content ) = @_;
404 0         0 my $response =
405             $this->{_request}
406             ->insert( $API_DOMAIN_URI . 'action/GetUploadToken', $content );
407              
408 0         0 my ( $url, $token ) =
409             $response =~ m/(.+?)<\/url>(.+?)<\/token>/;
410 0 0       0 if ( $this->next_url ) {
411 0         0 $url .= '?' . $this->next_url;
412             }
413 0         0 return ( $url, $token, $response );
414             }
415              
416             #TODO:move this and rewrite from scratch!
417             sub direct_uploading {
418 0     0 0 0 my ( $this, $uri, $content ) = @_;
419              
420 0         0 my $binary = $this->_binary_data;
421              
422 0         0 my $content2 = <
423              
424             --f93dcbA3
425             Content-Type: application/atom+xml; charset=UTF-8
426              
427            
428            
429             xmlns:media="http://search.yahoo.com/mrss/"
430             xmlns:yt="http://gdata.youtube.com/schemas/2007">
431             XML
432              
433 0         0 $content2 .= $content . '';
434              
435 0         0 $content2 .= <
436              
437             --f93dcbA3
438             Content-Type: video/quicktime
439             Content-Transfer-Encoding: binary
440              
441             $binary
442              
443             --f93dcbA3--
444             XML
445              
446 0         0 my $req = HTTP::Request->new( POST => $uri );
447              
448 0 0       0 if ( $this->{_request}->auth ) {
449 0         0 $this->{_request}->auth->set_authorization_headers( $this, $req );
450 0         0 $this->{_request}->auth->set_service_headers( $this, $req );
451             }
452 0         0 $req->header( 'GData-Version' => $this->{_request}->query->get('v') );
453 0         0 $req->header( 'Slug' => $this->filename );
454 0         0 $req->content_type('multipart/related; boundary="f93dcbA3"');
455 0         0 $req->header( 'Content-Length' => length($content2) );
456 0         0 $req->header( 'Connection' => 'close' );
457 0         0 $req->content($content2);
458              
459 0         0 my $res = $this->{_request}->{__UA__}->request($req);
460 0 0       0 if ( $res->is_success ) {
461 0         0 return $this;
462             }
463             else {
464 0         0 die new WebService::GData::Error( $res->code, $res->content );
465             }
466              
467             }
468              
469             {
470 5     5   48 no strict 'refs';
  5         11  
  5         1705  
471              
472             my %controlList = (
473             videoRespond => 'video_response',
474             rate => 'rating',
475             embed => 'embedding',
476             list => 'listing',
477             syndicate => 'syndication'
478             );
479             my @ytControls = ( keys %controlList, 'comment', 'comment_vote' );
480              
481             foreach my $access (@ytControls) {
482             my $name = $access;
483             $name =~ s/_([a-z])/\U$1/g;
484             my $func = $controlList{$access} || $access;
485             *{ __PACKAGE__ . '::is_' . $func . '_allowed' } = sub {
486 3     3   7 my $this = shift;
487 3         9 my $ret = $this->_access_control->action($name)->[0];
488 3 100 66     15 return ( $ret && $ret->permission eq 'allowed' ) ? 1 : 0;
489             }
490             }
491             }
492              
493             private _urlencode => sub {
494             my ($string) = shift;
495             $string =~ s/(\W)/"%" . unpack("H2", $1)/ge;
496             return $string;
497             };
498              
499             "The earth is blue like an orange.";
500              
501             __END__