File Coverage

blib/lib/Video/CPL.pm
Criterion Covered Total %
statement 230 634 36.2
branch 51 280 18.2
condition 6 73 8.2
subroutine 46 91 50.5
pod 62 75 82.6
total 395 1153 34.2


line stmt bran cond sub pod time code
1             package Video::CPL;
2              
3 1     1   30043 use warnings;
  1         1  
  1         32  
4 1     1   3 use strict;
  1         1  
  1         18  
5 1     1   3 use Carp;
  1         4  
  1         44  
6 1     1   455 use Encode;
  1         7535  
  1         65  
7 1     1   6 use Data::Dumper;
  1         1  
  1         33  
8 1     1   3 use Fcntl;
  1         1  
  1         172  
9 1     1   613 use File::Temp;
  1         15100  
  1         77  
10 1     1   5 use File::Path qw(make_path);
  1         1  
  1         38  
11 1     1   3 use List::Util qw(shuffle);
  1         1  
  1         66  
12 1     1   400 use LWP::Simple;
  1         42360  
  1         11  
13 1     1   278 use XML::Simple;
  1         2  
  1         8  
14 1     1   555 use XML::Writer;
  1         6712  
  1         28  
15              
16 1     1   510 use Video::CPL::Cue;
  1         2  
  1         25  
17 1     1   6 use Video::CPL::Annotation;
  1         1  
  1         15  
18 1     1   3 use Video::CPL::Story;
  1         1  
  1         15  
19 1     1   359 use Video::CPL::Layout;
  1         2  
  1         5402  
20              
21             our $VAR1; #useful for evals
22              
23             =head1 NAME
24              
25             Video::CPL - Create and manipulate Coincident TV Programming Language (CPL) files.
26              
27             =head1 VERSION
28              
29             Version 0.09
30              
31             =cut
32              
33             our $VERSION = '0.09';
34              
35             =head1 SYNOPSIS
36              
37             Video::CPL provides an object-oriented module for creating CPL files. CPL files control interactive video experiences.
38              
39             A simple example might be displaying a video, e.g. from Youtube, in a player on a webpage.
40              
41             A more complex example might include images, which the user clicks on to jump to other videos.
42              
43             In conjunction with CGI.pm it is straightforward to create fully interactive web pages with dynamically created video experiences.
44              
45             Video::CPL does not create the video file itself; it works with videos on services such as Youtube, or created with tools such as Video::FFmpeg.
46              
47             A tutorial is available at http://metabase.coincident.tv/cpan.
48              
49             Short code sample: create a file using CPL, and then embed a link to the file in your html as shown below.
50              
51             use CPL;
52             my $ctv = new Video::CPL(videoSource=>"http://www.youtube.com/watch?v=0ZexPPDLXRA"
53             html=>$htmlfilelocation);
54             $ctv->programEnd(30); #end after 30 seconds
55             print $ctv->print(); #prints out cpl file
56              
57             #and then, when writing html:
58             print $ctv->embed(); #print out an HTML embed pointing to the temporary file
59              
60             =head1 METHODS
61              
62             =head2 new(videoSource=>$url,[html=>$htmldir,ref=>$url,] [PARMS]);
63              
64             Create a new Video::CPL object. There is one videoSource per Video::CPL object.
65              
66             Attributes are named the same as in CPL. E.g. to set xUniqueID add "xUniqueID=>23"
67              
68             If the CPL for this object will be given in response to a CGI query, there may be no need for a file. Otherwise, in most simple cases, a CPL file will be written out to a local file which is can then be referred to with a URL.
69              
70             To do this CPL needs to be told of a directory which can be seen via HTTP, i.e., one in the document tree of the web server. The directory on the local file system should be speficied either in the CPLDIR environment variable, or in the html parameter. The URL for this same directory should be specified in the CPLURL environment variable, or in the ref parameter.
71              
72             CPL can be used to build up a new CPL, or it can initialize itself with an existing CPL file.
73              
74             If attributes are given which are not in the 0.7 or 0.8 spec, they will generate an error.
75              
76             Other parameters can include:
77              
78             validator=>"CPL_v0.7_validator.xsd" . Sets the XML validator. Advanced users only.
79              
80             xsi=>"http://www.w3.org/2001/XMLSchema-instance" . Set the schema instance. Advanced users only.
81              
82             initfromctv=>"foo.ctv" . Given a string which is either valid XML or a filename, use it to initialize the Video::CPL object.
83              
84             =cut
85              
86             sub new {
87 2     2 1 70571 my $pkg = shift;
88 2         23 my %parms = @_;
89 2         7 my $ret = {};
90 2         7 bless $ret,$pkg;
91              
92 2   50     25 $ret->{xVersionCPL} = $parms{xVersionCPL} || "0.8.0";
93              
94             #The directory on the host system which is the top level html directory.
95             #normally used in embed when printing out the html.
96             #If not used, i.e. if you are collecting the ctv and placing it yourself, this can be invalid.
97 2   33     13 $ret->{html} = $parms{html} || $ENV{CPLDIR};
98              
99             #ref is the base of the URL to be used for intra-ctv references. It should point to the same location as html
100 2   33     11 $ret->{ref} = $parms{ref} || $ENV{CPLURL};
101              
102             #XML-isms
103 2 50 33     20 $ret->{'xsi:noNamespaceSchemaLocation'} = $parms{'xsi:noNamespaceSchemaLocation'} || ($ret->{xVersionCPL} eq "0.7.0")?"CPL_v0.7_validator.xsd":"CPL_v0.8_validator.xsd";
104 2   50     9 $ret->{'xmlns:xsi'} = $parms{'xmlns:xsi'} || "http://www.w3.org/2001/XMLSchema-instance";
105              
106 2 100       10 $ret->parsectv($parms{initfromctv}) if $parms{initfromctv};
107             #check parameters; confess on typos etc.
108 2         9 foreach my $q (keys %parms){
109 17 50       55 next if $q =~ /xVersionCPL|videoSource|videoWidth|videoHeight|backgroundHTML|xWebServiceLoc|xUniqueID|xProgLevelDir|loggingService|html|ref|xsi:noNamespaceSchemaLocation|xmlns:xsi|xProgLevelData|skinButtons|webViewLayout|videoViewLayout|frameWidth|frameHeight|videoX|videoY|youtubeID|initfromctv/;
110 0         0 confess("new CPL does not know what to do with parameter($q)\n");
111             }
112              
113 2         5 foreach my $q (qw(xVersionCPL videoSource videoWidth videoHeight backgroundHTML xWebServiceLoc xUniqueID xProgLevelDir loggingService xProgLevelData skinButtons webViewLayout videoViewLayout frameWidth frameHeight videoX videoY youtubeID)){
114 36 100       54 $ret->{$q} = $parms{$q} if defined($parms{$q});
115             }
116              
117             #Errors: no source file specified
118             #Error: parameter given but not used
119             ##Errors: no source file specified
120             #Error: parameter given but not used
121 2 100       7 if (!defined($parms{initfromctv})){
122 1         2 $ret->{cuePoints} = [];
123 1         11 $ret->{cuePoints}[0] = Video::CPL::Cue->new(name=>"CPLBegin",time=>0,cueType=>"regular",parent=>$ret);
124             }
125              
126 2 50       8 if (exists($parms{htmlurl})){
    50          
127 0   0     0 $ret->{htmlrel} = $parms{htmlrel} || ".";
128 0         0 $ret->{htmlurl} = $parms{htmlurl};
129             } elsif (defined($ret->{html})){
130             #open up a temporary file now to avoid race conditions. Might not even be used.
131 0         0 my $fh = File::Temp->new(UNLINK=>0,DIR=>"$ret->{html}",SUFFIX=>".ctv");
132 0         0 my $filename = $fh->filename;
133 0         0 $ret->{fullfilename} = $filename;
134 0         0 chmod 0644,$filename;
135 0         0 $filename =~ s/.*\///;
136 0         0 $ret->{ctvfilename} = $filename;
137 0         0 $ret->{fh} = $fh;
138             }
139 2         6 return $ret;
140             }
141              
142             sub datepref {
143 0     0 0 0 my $x = shift;
144 0         0 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($x);
145 0         0 $year+=1900;
146 0         0 $mon++;
147 0 0       0 $mon = "0".$mon if length($mon) == 1;
148 0 0       0 $mday = "0".$mday if length($mday) == 1;
149 0         0 return "$year-$mon-$mday";
150             }
151              
152             sub fixfile {
153 0     0 0 0 my $obj = shift;
154 0 0       0 return if $obj->{filefixed};
155 0         0 my $dseg = datepref($^T); #convert time of script starting to 2010-01-01 format
156 0 0       0 $obj->{htmlrel} = "." if !exists($obj->{htmlrel});
157 0         0 my $dir = $obj->{htmlrel}."/".$dseg;
158 0 0       0 make_path($dir) if !-d $dir;
159 0 0       0 confess("$0: Video::CPL Fatal Error. Could not create or access directory ($dir).\n") if !-d $dir;
160             #create file with File::Temp chmod 0644 etc.
161 0         0 my $fh = File::Temp->new(UNLINK=>0,DIR=>$dir,SUFFIX=>".ctv");
162 0         0 $obj->{fh} = $fh;
163 0         0 my $filename = $fh->filename;
164 0         0 chmod 0644,$filename;
165 0         0 $filename =~ s/.*\///;
166 0         0 $obj->{ctvfilename} = $filename;
167 0         0 $obj->{relfilename} = "$dir/$filename";
168 0         0 $obj->{relfilename} =~ s/^\.\///; #if we are ./foo.ctv become foo.ctv
169 0         0 $obj->{relfilename} =~ s/\/\//\//g;
170 0         0 $obj->{url} = $obj->{htmlurl}."/$dseg/$filename";
171             #set full relative pathname and fh
172 0         0 $obj->{filefixed} = 1;;
173 0         0 return;
174             }
175              
176             =head asurl()
177              
178             Returns the URL which will recreate this URL. This will be a file or a reference to this script.
179              
180             Used when creating the media parameter for the CTV player, which requires a full URL.
181              
182             =cut
183              
184             sub asurl {
185 0     0 0 0 my $obj = shift;
186              
187 0 0       0 confess("$0: Fatal Error. URL reference to a Video::CPL object which is marked 'noref'.\n") if $obj->{noref};
188              
189 0 0       0 if ($obj->{isdyn}){
190 0         0 confess("NYI");
191             } else {
192 0 0       0 $obj->fixfile() if !$obj->{filefixed};
193 0         0 return $obj->{url};
194             }
195             }
196              
197             =head asrel([$otherCPL])
198              
199             Returns the URL fragment (e.g. foo/goo.ctv) which will access this file from the current directory. If given
200             a parameter of another Video::CPL object, will return a URL fragment which is valid in that objects context.
201              
202             =cut
203              
204             sub asrel {
205 0     0 0 0 my $obj = shift;
206             #optional second param. Return from the perspective of this cpl.
207 0 0       0 confess("$0: Fatal Error. URL reference to a Video::CPL object which is marked 'noref'.\n") if $obj->{noref};
208              
209 0 0       0 if ($obj->{isdyn}){
210 0         0 confess("NYI");
211             } else {
212 0 0       0 $obj->fixfile() if !$obj->{filefixed};
213 0         0 return $obj->{relfilename};
214             }
215             }
216              
217             sub asreldir {
218 0     0 0 0 my $obj = shift;
219 0         0 my $x = $obj->asrel();
220 0 0       0 return $1 if $x =~ /^(.*)\//; #normal case, foo/goo/hoo.ctv, return foo/goo
221 0         0 return "."; #if there was no slash, current directory
222             }
223              
224             sub asreltail {
225 0     0 0 0 my $obj = shift;
226 0         0 my $x = $obj->asrel();
227 0 0       0 return $1 if $x =~ /^.*\/(.*)$/; #normal case, foo/goo/hoo.ctv, return hoo.ctv
228 0         0 return "$x"; #if there was no slash, return self
229             }
230              
231             sub reffrom {
232 0     0 0 0 my $obj = shift;
233 0         0 my $from = shift;
234 0 0 0     0 if ($from && ($from != $obj)){
235 0 0       0 if ($obj->{noref}){
236 0         0 warn("Video::CPL: this object has noref set, cannot be referenced from other objects, fatal error.\n");
237 0         0 return undef;
238             }
239             } else {
240 0         0 return $obj->{tail};
241             }
242             }
243              
244             #=head2 tmpfile(ext=>".ctv")
245             #
246             # Create a temporary file. Return the filehandle, and an address usable in the current CTV.
247             #
248             #=cut
249             #
250             #sub tmpfile {
251             # my $obj = shift;
252             # my %p = @_;
253             # confess; #this is a placeholder
254             #}
255              
256             =head2 xVersionCPL([$string])
257              
258             Accessor routine to set or read xVersionCPL.
259              
260             =cut
261              
262 2 50   2 1 2 sub xVersionCPL { my $obj = shift; $obj->{xVersionCPL} = shift if @_; return $obj->{xVersionCPL};};
  2         6  
  2         11  
263              
264             =head2 videoSource([$url])
265              
266             Accessor routine to set or read videoSource.
267              
268             =cut
269              
270 2 50   2 1 523 sub videoSource { my $obj = shift; $obj->{videoSource} = shift if @_; return $obj->{videoSource};};
  2         6  
  2         8  
271              
272             =head2 xWebServiceLoc([$url])
273              
274             Accessor routine to set or read xWebServiceLoc.
275              
276             =cut
277              
278 2 50   2 1 3 sub xWebServiceLoc { my $obj = shift; $obj->{xWebServiceLoc} = shift if @_; return $obj->{xWebServiceLoc};};
  2         6  
  2         7  
279              
280             =head2 loggingService([$url])
281              
282             Accessor routine to set or read loggingService.
283              
284             =cut
285              
286 2 50   2 1 2 sub loggingService { my $obj = shift; $obj->{loggingService} = shift if @_; return $obj->{loggingService};};
  2         6  
  2         6  
287              
288             =head2 skinButtons([$url])
289              
290             Accessor routine to set or read skinButtons. These are optional, and used to form the control bar.
291              
292             =cut
293              
294 2 50   2 1 3 sub skinButtons { my $obj = shift; $obj->{skinButtons} = shift if @_; return $obj->{skinButtons};};
  2         7  
  2         7  
295              
296             =head2 backgroundHTML([$url])
297              
298             Accessor routine to set or read backgroundHTML.
299              
300             =cut
301              
302 2 50   2 1 3 sub backgroundHTML { my $obj = shift; $obj->{backgroundHTML} = shift if @_; return $obj->{backgroundHTML};};
  2         8  
  2         7  
303              
304             =head2 videoWidth([$string])
305              
306             Accessor routine to set or read videoWidth.
307              
308             =cut
309              
310 2 50   2 1 3 sub videoWidth { my $obj = shift; $obj->{videoWidth} = shift if @_; return $obj->{videoWidth};};
  2         7  
  2         7  
311              
312             =head2 videoHeight([$string])
313              
314             Accessor routine to set or read videoHeight.
315              
316             =cut
317              
318 2 50   2 1 3 sub videoHeight { my $obj = shift; $obj->{videoHeight} = shift if @_; return $obj->{videoHeight};};
  2         5  
  2         8  
319              
320             =head2 frameWidth([$string])
321              
322             Accessor routine to set or read frameWidth.
323              
324             =cut
325              
326 2 50   2 1 3 sub frameWidth { my $obj = shift; $obj->{frameWidth} = shift if @_; return $obj->{frameWidth};};
  2         9  
  2         7  
327              
328             =head2 frameHeight([$string])
329              
330             Accessor routine to set or read frameHeight.
331              
332             =cut
333              
334 2 50   2 1 4 sub frameHeight { my $obj = shift; $obj->{frameHeight} = shift if @_; return $obj->{frameHeight};};
  2         5  
  2         8  
335              
336             =head2 videoX([$string])
337              
338             Accessor routine to set or read videoX.
339              
340             =cut
341              
342 2 50   2 1 7 sub videoX { my $obj = shift; $obj->{videoX} = shift if @_; return $obj->{videoX};};
  2         6  
  2         13  
343              
344             =head2 videoY([$string])
345              
346             Accessor routine to set or read videoY.
347              
348             =cut
349              
350 2 50   2 1 3 sub videoY { my $obj = shift; $obj->{videoY} = shift if @_; return $obj->{videoY};};
  2         6  
  2         8  
351              
352             =head2 videoViewLayout([$layoutname])
353              
354             Accessor routine to set or read videoViewLayout.
355              
356             =cut
357              
358 2 50   2 1 2 sub videoViewLayout { my $obj = shift; $obj->{videoViewLayout} = shift if @_; return $obj->{videoViewLayout};};
  2         7  
  2         7  
359              
360             =head2 webViewLayout([$layoutname])
361              
362             Accessor routine to set or read webViewLayout.
363              
364             =cut
365              
366 2 50   2 1 2 sub webViewLayout { my $obj = shift; $obj->{webViewLayout} = shift if @_; return $obj->{webViewLayout};};
  2         8  
  2         6  
367              
368             =head2 youtubeID([$id])
369              
370             Accessor routine to set or read youtubeID.
371              
372             =cut
373              
374 2 50   2 1 4 sub youtubeID { my $obj = shift; $obj->{youtubeID} = shift if @_; return $obj->{youtubeID};};
  2         6  
  2         8  
375              
376             =head2 xUniqueID([$id])
377              
378             Accessor routine to set or read xUniqueID.
379              
380             =cut
381              
382 2 50   2 1 3 sub xUniqueID { my $obj = shift; $obj->{xUniqueID} = shift if @_; return $obj->{xUniqueID};};
  2         6  
  2         8  
383              
384             =head2 xProgLevelDir([$id])
385              
386             Accessor routine to set or read xProgLevelDir.
387              
388             =cut
389              
390 2 50   2 1 2 sub xProgLevelDir { my $obj = shift; $obj->{xProgLevelDir} = shift if @_; return $obj->{xProgLevelDir};};
  2         6  
  2         7  
391              
392             =head2 video([$video1,$video2,$video3}])
393              
394             Accessor routine for the video field, which contains an array of three different video sources which can be used.
395             Returns an array, or undef if it has not been set.
396             Takes an array of 3 urls.
397             There is no way to set an individual URL with this accessor; read and then write to set one.
398              
399             =cut
400              
401             sub video {
402 0     0 1 0 my $obj = shift;
403 0 0       0 if (@_){
404 0 0       0 if ($obj->{video}){
405 0         0 my @a = @{$obj->{video}[0]{source}};
  0         0  
406 0         0 my @new = @_;
407             #who knows if the number of video fields will change in the future
408 0 0 0     0 confess("confused on video a $#a new $#new\n") if ($#a != $#new) && (($#a != 2) || ($#new != 2));
      0        
409 0         0 foreach my $ia (0..$#a){
410 0         0 my %h = %{$a[$ia]};
  0         0  
411 0         0 $h{src} = $new[$ia];
412             }
413 0         0 return @new;
414             } else {
415 0         0 $obj->{video} = [{source => []}];
416 0         0 foreach my $s (@_){
417 0         0 push @{$obj->{video}[0]{source}},{src => $s};
  0         0  
418             }
419 0         0 return @_;
420             }
421             } else {
422 0 0       0 return undef if !$obj->{video};
423 0         0 my @a = @{$obj->{video}[0]{source}};
  0         0  
424 0         0 my @ret;
425 0         0 foreach my $ia (0..$#a){
426 0         0 my %h = %{$a[$ia]};
  0         0  
427 0         0 push @ret,$h{src};
428             }
429 0         0 return @ret;
430             }
431             }
432              
433             =head2 tl($cuepointname)
434            
435             Create a targetList with one element.
436              
437             =cut
438              
439             sub tl {
440 1     1 1 6 my $obj = shift;
441 1         2 my $s = shift;
442 1         7 my $t = new Video::CPL::Target(cuePointRef=>$s);
443 1         9 my $tl = new Video::CPL::TargetList(target=>[$t]);
444             #print "tl returning:\n",Dumper($tl);
445 1         4 return $tl;
446             }
447              
448             =head2 parsectv()
449              
450             =cut
451              
452             my @FIELDS = qw(backgroundHTML frameHeight frameWidth loggingService skinButtons
453             videoHeight videoSource videoViewLayout videoWidth videoX videoY
454             webViewLayout xProgLevelData xProgLevelDir xUniqueID xVersionCPL
455             xWebServiceLoc xmlns:xsi xsi:noNamespaceSchemaLocation youtubeID);
456             my @MPFIELDS = qw(xmlns:xsi xsi:noNamespaceSchemaLocation);
457             my @PLMFIELDS = qw(backgroundHTML frameHeight frameWidth loggingService skinButtons
458             videoHeight videoSource videoViewLayout videoWidth videoX videoY
459             webViewLayout xProgLevelData xProgLevelDir xUniqueID xVersionCPL
460             xWebServiceLoc youtubeID);
461              
462             sub parsectv {
463 1     1 1 2 my $obj = shift;
464 1         2 my $initfromctv = shift;
465 1         7 my $ref = XMLin($initfromctv,ForceArray=>1,KeyAttr=>{},KeepRoot=>1);
466 1         17167 my %d = %{$ref};
  1         3  
467 1         4 $obj->{'xsi:noNamespaceSchemaLocation'} = $d{"xsi:noNamespaceSchemaLocation"};
468 1         3 $obj->{'xmlns:xsi'} = $d{"xmlns:xsi"};
469 1         3 foreach my $q (@MPFIELDS){
470 2 50       12 $obj->{$q} = $d{MediaProgram}[0]{$q} if defined($d{MediaProgram}[0]{$q});
471             }
472 1         3 foreach my $q (@PLMFIELDS){
473 18 100       61 $obj->{$q} = $d{MediaProgram}[0]{progLevelMetadata}[0]{$q} if defined($d{MediaProgram}[0]{progLevelMetadata}[0]{$q});
474             }
475 1         2 foreach my $q ("video"){
476 1 50       25 $obj->{$q} = $d{MediaProgram}[0]{progLevelMetadata}[0]{$q} if defined($d{MediaProgram}[0]{progLevelMetadata}[0]{$q});
477             }
478             #$obj->{video} = $d{MediaProgram}[0]{progLevelMetadata[0]{video}
479             #process annotations
480 1 50       3 if (defined $d{MediaProgram}[0]{annotations}){
481 1         1 my @a;
482 1         2 foreach my $x (@{$d{MediaProgram}[0]{annotations}[0]{annotation}}){
  1         5  
483 1         6 push @a,Video::CPL::Annotation::fromxml($obj,$x);
484             }
485 1         4 $obj->{annotations} = \@a;
486             }
487              
488             #process layouts
489 1 50       4 if (defined $d{MediaProgram}[0]{layouts}){
490 1         2 my @l;
491 1         2 foreach my $x (@{$d{MediaProgram}[0]{layouts}[0]{layout}}){
  1         6  
492 2         16 push @l,Video::CPL::Layout::fromxml($x);
493             }
494 1         3 $obj->{layouts} = \@l;
495             }
496              
497             #process cuePoints
498 1 50       3 if (defined $d{MediaProgram}[0]{cuePoints}){
499 1         2 foreach my $x (@{$d{MediaProgram}[0]{cuePoints}[0]{cuePt}}){
  1         4  
500 1         5 $obj->addcue(Video::CPL::Cue::fromxml($obj,$x));
501             }
502             }
503              
504             #process webPoints
505 1 50       13 if (defined $d{MediaProgram}[0]{webPoints}){
506 0         0 foreach my $x (@{$d{MediaProgram}[0]{webPoints}[0]{cuePt}}){
  0         0  
507 0         0 $obj->addwp(Video::CPL::Cue::fromxml($obj,$x));
508             }
509             }
510             #done
511             }
512              
513             =head2 newname("base")
514              
515             Returns a name of the form "basedddddddd" which is not used by any other cue point or annotation in this CPL object.
516              
517             =cut
518              
519             sub newname {
520 0     0 1 0 my $obj = shift;
521 0   0     0 my $base = shift || "name";
522 0         0 my $name;
523 0   0     0 do {
524 0         0 $name = $base.int(rand(100000000));
525             } until ! defined($obj->cuebyname($name)) && !defined($obj->annobyname($name));
526 0         0 return $name;
527             }
528              
529              
530             =head2 addcue($cue);
531              
532             Adds the created cue to the CPL object. This is not needed if reading the cue points from the video file itself, e.g. with a local .flv file.
533              
534             =cut
535              
536             sub addcue {
537 1     1 1 2 my $obj = shift;
538 1         1 my $cue = shift;
539 1         2 $cue->parent($obj);
540 1 50       4 confess("addcue needs a time in cuePt\n") if !defined($cue->time());
541 1         1 push @{$obj->{cuePoints}},$cue;
  1         2  
542 1         1 my @a = @{$obj->{cuePoints}};
  1         3  
543 1 50       2 if ($#a > 0){
544 0         0 my $last = $a[$#a];
545 0         0 my $nexttolast = $a[$#a-1];
546 0 0       0 if ($last->time() <= $nexttolast->time()) {
547 0         0 @{$obj->{cuePoints}} = sort {$a->time() <=> $b->time()} @a;
  0         0  
  0         0  
548             }
549             }
550 1         3 return $cue;
551             }
552              
553             sub getcptimes {
554 0     0 0 0 my $obj = shift;
555 0         0 my @ret;
556 0 0       0 return @ret if !exists($obj->{cuePoints});
557 0         0 my @a = @{$obj->{cuePoints}};
  0         0  
558 0         0 foreach my $a (@a){
559 0         0 push @ret, $a->time();
560             }
561 0         0 return @ret;
562             }
563              
564             sub cuetostring {
565 0     0 0 0 my $obj = shift;
566 0         0 my $cue = shift;
567 0 0       0 if ($cue->parent() == $obj){
568 0         0 return "#".$cue->name();
569             }
570             #if same object then #foo
571             #if same directory (think: dynamic, html and ref but no file) then foo.ctv#foo
572             #think. user specified full html and ref or no ref. Only need http if not specified.
573             #else full url
574 0         0 confess;
575             }
576              
577             sub annotostring {
578 0     0 0 0 my $obj = shift;
579 0         0 my $anno = shift;
580             #see comments for cuetostring re local versus same versus remote
581 0         0 confess;
582             }
583              
584             sub converttarget {
585 0     0 0 0 my $obj = shift;
586 0         0 my %p = @_;
587 0 0       0 return %p if !exists $p{target};
588 0 0       0 confess("converttarget given a null target\n") if !defined $p{target};
589 0         0 my $t = $p{target};
590 0         0 my @a;
591 0 0       0 if (ref($t) eq "ARRAY"){
592 0         0 @a = @{$t}
  0         0  
593             } else {
594 0         0 push @a,$t;
595             }
596 0         0 delete $p{target};
597 0         0 $p{targetList} = new Video::CPL::TargetList();
598 0 0       0 if (defined $p{backgroundPicLoc}){
599 0         0 $p{targetList}->backgroundPicLoc($p{backgroundPicLoc});
600 0         0 delete $p{backgroundPicLoc};
601             }
602 0 0       0 if (defined $p{headerText}){
603 0         0 $p{targetList}->headerText($p{headerText});
604 0         0 delete $p{headerText};
605             }
606 0 0       0 if (defined $p{operation}){
607 0         0 $p{targetList}->operation($p{operation});
608 0         0 delete $p{operation};
609             }
610 0         0 my @t;
611 0         0 foreach my $q (@a){
612 0         0 my $s;
613 0 0       0 if (ref($q) eq "Video::CPL::Cue"){
    0          
614 0         0 $s = $q->reffromobj($obj);
615             } elsif (ref($q) eq "Video::CPL::Annotation"){
616 0         0 $s = $q->reffromobj($obj);
617             } else {
618             #confess if not scalar.
619 0         0 $s = $q;
620             }
621 0         0 push @t,new Video::CPL::Target(cuePointRef=>$s);
622             }
623 0         0 $p{targetList}->target(\@t);
624 0         0 return %p;
625             }
626              
627             sub convertstory {
628 0     0 0 0 my $obj = shift;
629 0         0 our @FIELDS = qw(balloonText forever picLoc picOverLoc);
630 0         0 my %p = @_;
631             return %p if !defined($p{forever}) && !defined($p{balloonText}) &&
632 0 0 0     0 !defined($p{picLoc}) && !defined($p{picOverLoc});
      0        
      0        
633 0         0 $p{story} = new Video::CPL::Story();
634 0 0       0 if (defined($p{forever})){
635 0         0 $p{story}->forever($p{forever});
636 0         0 delete $p{forever};
637             }
638 0 0       0 if (defined($p{balloonText})){
639 0         0 $p{story}->balloonText($p{balloonText});
640 0         0 delete $p{balloonText};
641             }
642 0 0       0 if (defined($p{picLoc})){
643 0         0 $p{story}->picLoc($p{picLoc});
644 0         0 delete $p{picLoc};
645             }
646 0 0       0 if (defined($p{picOverLoc})){
647 0         0 $p{story}->picOverLoc($p{picOverLoc});
648 0         0 delete $p{picLoc};
649             }
650 0         0 return %p;
651             }
652              
653             =head2 newcue(%cueparms)
654              
655             Create a new Cue point with the given parameters, and set the parent to this CPL object.
656              
657             =cut
658              
659             sub newcue {
660 0     0 1 0 my $obj = shift;
661 0         0 my %parms = @_;
662 0 0       0 confess("newcue needs a time in cuePt\n") if !defined($parms{'time'});
663 0 0       0 $parms{name} = $obj->newname("cue") if !defined($parms{name});
664             #convert target to a targetlist with one entry. If there are other Targetlist
665 0 0       0 %parms = $obj->converttarget(%parms) if defined $parms{target};
666 0 0 0     0 %parms = $obj->convertstory(%parms) if defined($parms{picLoc}) || defined($parms{picOverLoc}) || defined($parms{balloonText});
      0        
667 0         0 my $ret = new Video::CPL::Cue(%parms);
668 0         0 return $obj->addcue($ret);
669             }
670              
671             =head2 story(text=>"some text",pic=>"foo.jpg")
672              
673             Shorthand to create a Video::CPL::Story object.
674              
675             =cut
676              
677             sub story {
678 0     0 1 0 my $obj = shift;
679 0         0 my %parms = @_;
680 0         0 my $ret = Video::CPL::Story->new(%parms);
681             # my $text = $parms{balloonText};
682             # my $pic = $parms{picLoc};
683             # my $ret;
684             # if ($pic){
685             # if ($text){
686             # } else {
687             # $ret = Video::CPL::Story->new(picLoc=>$pic);
688             # }
689             # } else {
690             # $ret = Video::CPL::Story->new(balloonText=>$text);
691             # }
692 0         0 return $ret;
693             }
694              
695             =head2 layout(%parms)
696              
697             Create a new layout and install it in this CPL. Pass the parameters on to Video::CPL::Layout::new.
698              
699             =cut
700              
701             sub layout {
702 2     2 1 440 my $obj = shift;
703 2         11 my %parms = @_;
704 2         14 my $ret = new Video::CPL::Layout(%parms);
705 2         3 push @{$obj->{layouts}},$ret;
  2         4  
706 2         4 return $ret;
707             }
708              
709             =head2 layoutbyname($name)
710              
711             Return the layout with the given name.
712              
713             =cut
714              
715             sub layoutbyname {
716 64     64 1 94 my $obj = shift;
717 64         57 my $name = shift;
718 64 50       101 return undef if !defined($obj->{layouts});
719 64         41 my @l = @{$obj->{layouts}};
  64         95  
720 64         67 foreach my $l (@l){
721 64 50       111 return $l if $l->name() eq $name;
722             }
723 0         0 return undef;
724             }
725              
726             =head2 allstories()
727              
728             Return an array with all of the Annotation based Story objects in this CPL.
729              
730             =cut
731              
732             sub allstories {
733 0     0 1 0 my $obj = shift;
734 0         0 my @ret = ();
735 0         0 my @a = $obj->annotations();
736 0         0 foreach my $a (@a){
737 0 0       0 push @ret,$a->story() if $a->story();
738             }
739 0         0 return @ret;
740             }
741              
742             =head2 numcue($k)
743              
744             Returns the k-th cuePoint Cue object. Note that a normally created CPL object will always create a cue point a the beginning, easily obtained with firstcue(), below.
745              
746             =cut
747              
748             sub numcue {
749 0     0 1 0 my $obj = shift;
750 0         0 my $num = shift;
751 0 0       0 if (defined($obj->{cuePoints}[$num])){
752 0         0 return $obj->{cuePoints}[$num];
753             }
754 0         0 return undef;
755             }
756              
757             =head2 numwebcue(4)
758              
759             Returns the 4th webPoint object.
760              
761             =cut
762              
763             sub numwebcue {
764 0     0 1 0 my $obj = shift;
765 0         0 my $num = shift;
766 0 0       0 if (defined($obj->{webPoints}[$num])){
767 0         0 return $obj->{webPoints}[$num];
768             }
769 0         0 die "Error: There is no web cue numbered $num for ($obj->{videoSource}). Does it not have any web cue points?\n";
770 0         0 return undef;
771             }
772              
773             =head2 maxweb()
774              
775             =cut
776              
777             sub maxweb {
778 0     0 1 0 my $obj = shift;
779 0         0 my @a = @{$obj->{webPoints}};
  0         0  
780 0         0 return $#a;
781             }
782              
783             =head2 max()
784              
785             =cut
786              
787             sub max {
788 0     0 1 0 my $obj = shift;
789 0         0 my @a = @{$obj->{cuePoints}};
  0         0  
790 0         0 return $#a;
791             }
792              
793             =head2 firstcue()
794              
795             Returns the first cuePoint Cue object.
796              
797             =cut
798              
799             sub firstcue {
800 0     0 1 0 my $obj = shift;
801 0 0       0 if (defined($obj->{cuePoints}[0])){
802 0         0 return $obj->{cuePoints}[0];
803             }
804 0         0 die "Error: There is no first cue for ($obj->{videoSource}). Does it not have any cue points?\n";
805 0         0 return undef;
806             }
807              
808             =head2 lastcue()
809              
810             =cut
811              
812             sub lastcue {
813 0     0 1 0 my $obj = shift;
814 0         0 my @a = @{$obj->{cuePoints}};
  0         0  
815 0 0       0 return $a[$#a] if $#a > -1;
816 0         0 die "Error: There is no last cue (last is $#a) for ($obj->{videoSource}). Does it not have any cue points?\n";
817 0         0 return undef;
818             }
819              
820             =head2 add()
821             Adds a cue point to the end of the cue point list. The parent of the cue point should either
822             not be set, or be correctly set. Cue points may only have one parent, and can therefore not be used
823             in multiple CPL objects.
824              
825             =cut
826              
827             sub add {
828 0     0 1 0 my $obj = shift;
829 0         0 my $cue = shift;
830 0 0 0     0 confess("adding a cue that belongs to someone else\n") if $cue->{parent} && ($cue->{parent} ne $obj);
831 0         0 $cue->{parent} = $obj;
832 0         0 push @{$obj->{cuePoints}},$cue;
  0         0  
833             }
834              
835             =head2 webPoints() return all webPoints for the current Video::CPL as an array
836              
837             =cut
838              
839             sub webPoints {
840 0     0 1 0 my $obj = shift;
841 0 0       0 return () if !exists($obj->{webPoints});
842 0         0 return @{$obj->{webPoints}};
  0         0  
843             }
844              
845             =head2 addwp()
846              
847             =cut
848              
849             sub addwp {
850 0     0 1 0 my $obj = shift;
851 0         0 my $cue = shift;
852 0         0 $cue->{parent} = $obj;
853 0         0 push @{$obj->{webPoints}},$cue;
  0         0  
854             }
855              
856             =head2 addanno()
857              
858             =cut
859              
860             sub addanno {
861 1     1 1 1 my $obj = shift;
862 1         7 my $anno = shift;
863 1         2 push @{$obj->{annotations}},$anno;
  1         5  
864 1         2 return $anno;
865             }
866              
867             =head2 annotations()
868              
869             =cut
870              
871             sub annotations {
872 0     0 1 0 my $obj = shift;
873 0 0       0 return @{$obj->{annotations}} if defined($obj->{annotations});
  0         0  
874 0         0 return ();
875             }
876              
877             =head2 webPoint(name=>"aname",interestURL=>"http://somewhere.com/foo.jpg",story=>{picLoc=>"foo.jpg"},tl=>[$target]});
878              
879             Create and add a new webPoint object.
880              
881             Target can be created with something like Video::CPL::Cue->new("Lost A");
882              
883             Story is currently just an anonymous hash. It may become an object in a future release.
884              
885             =cut
886              
887             sub webPoint {
888 0     0 1 0 my $obj = shift;
889 0         0 my %parms = @_;
890 0 0       0 $parms{name} = $obj->newname("webcue") if !defined($parms{name});
891 0 0 0     0 %parms = $obj->convertstory(%parms) if defined($parms{picLoc}) || defined($parms{picOverLoc}) || defined($parms{balloonText});
      0        
892 0         0 my $cue = Video::CPL::Cue->new(cueType=>'webPoint',%parms);
893 0         0 $obj->addwp($cue);
894 0         0 return $cue;
895             }
896              
897             =head2 goto(name=>"aname",tl=>[$target]});
898              
899             Create and add a new goto object.
900              
901             Target can be created with something like Video::CPL::Cue->new("Lost A");
902              
903             =cut
904              
905             sub goto {
906 0     0 1 0 my $obj = shift;
907 0         0 my %parms = @_;
908             #support old code for a while
909 0 0 0     0 if (defined($parms{dest}) && !defined($parms{target})){
910 0         0 $parms{target} = $parms{dest};
911 0         0 undef($parms{dest});
912             }
913 0 0 0     0 if (defined($parms{tl}) && !defined($parms{target})){
914 0         0 $parms{target} = shift @{$parms{tl}};
  0         0  
915 0         0 undef($parms{tl});
916             }
917 0 0       0 $parms{zeroLen} = "true" if !defined $parms{zeroLen};
918 0         0 return $obj->newcue(cueType=>'goto',%parms);
919             }
920              
921             =head2 regular(name=>"cuename",time=>1.0,interestURL=>"http://somewhere.com/foo.html");
922              
923             Create and add a new regular object.
924              
925             =cut
926              
927             sub regular {
928 0     0 1 0 my $obj = shift;
929 0         0 my %parms = @_;
930 0         0 return $obj->newcue(cueType=>'regular',%parms);
931             }
932              
933             =head2 insertPt(name=>"cuename",time=>1.0);
934              
935             Create and add a new regular object.
936              
937             =cut
938              
939             sub insertPt {
940 0     0 1 0 my $obj = shift;
941 0         0 my %parms = @_;
942 0         0 return $obj->newcue(cueType=>'insertPt',%parms);
943             }
944              
945             =head2 cuebyname($name)
946              
947             Return the first cue with the given name.
948              
949             =cut
950              
951             sub cuebyname {
952 56     56 1 75 my $obj = shift;
953 56         49 my $name = shift;
954 56   33     72 return $obj->cuePointbyname($name) || $obj->webPointbyname($name);
955             }
956              
957             =head2 cuePointbyname($name)
958              
959             Return the first cuePoint with the given name.
960              
961             =cut
962              
963             sub cuePointbyname {
964 56     56 1 45 my $obj = shift;
965 56         27 my $name = shift;
966 56 50       80 confess "cuePointbyname no name\n" if !$name;
967 56 50       77 if (defined($obj->{cuePoints})){
968 56         40 my @cuePoints = @{$obj->{cuePoints}};
  56         66  
969 56         53 foreach my $q (@cuePoints){
970 56 50       100 return $q if $q->name() eq $name;
971             }
972             }
973 0         0 return undef;
974             }
975              
976             =head2 webPointbyname($name)
977              
978             Return the first webPoint with the given name.
979              
980             =cut
981              
982             sub webPointbyname {
983 0     0 1 0 my $obj = shift;
984 0         0 my $name = shift;
985 0 0       0 if (defined($obj->{webPoints})){
986 0         0 my @webPoints = @{$obj->{webPoints}};
  0         0  
987 0         0 foreach my $q (@webPoints){
988 0 0       0 return $q if $q->name() eq $name;
989             }
990             }
991 0         0 return undef;
992             }
993              
994             =head2 cuebytime($time)
995              
996             Return the cuePt with the given time, or undef.
997              
998             =cut
999              
1000             sub cuebytime {
1001 0     0 1 0 my $obj = shift;
1002 0         0 my $time = shift;
1003 0 0       0 return undef if !defined($obj->{cuePoints});
1004 0         0 my @cuePoints = @{$obj->{cuePoints}};
  0         0  
1005 0         0 foreach my $q (@cuePoints){
1006 0 0       0 return $q if $q->time() eq $time;
1007             }
1008 0         0 return undef;
1009             }
1010              
1011              
1012             =head2 annobyname("name")
1013              
1014             Return the first annotation with the given name.
1015              
1016             =cut
1017              
1018             sub annobyname {
1019 44     44 1 74 my $obj = shift;
1020 44         35 my $name = shift;
1021 44 50       63 return undef if !defined $obj->{annotations};
1022 44         26 my @annos = @{$obj->{annotations}};
  44         58  
1023             #print STDERR "in annobyanme annos is ",Dumper(\@annos),"\n";
1024 44         49 foreach my $q (@annos){
1025 44 50       107 return $q if $q->name() eq $name;
1026             }
1027 0         0 return undef;
1028             }
1029              
1030             =head2 programEnd()
1031              
1032             =cut
1033              
1034             sub programEnd {
1035 0     0 1 0 my $obj = shift;
1036 0         0 my %parms = @_;
1037 0 0       0 $parms{zeroLen} = "true" if !defined $parms{zeroLen};
1038 0         0 return $obj->newcue(cueType=>'programEnd',%parms);
1039             }
1040              
1041             =head2 returnEnd()
1042              
1043             =cut
1044              
1045             sub returnEnd {
1046 0     0 1 0 my $obj = shift;
1047 0         0 my %parms = @_;
1048 0 0       0 $parms{zeroLen} = "true" if !defined $parms{zeroLen};
1049 0         0 return $obj->newcue(cueType=>'returnEnd',%parms);
1050             }
1051              
1052             =head2 annotation()
1053            
1054             Create an annotation and add it to the object.
1055              
1056             =cut
1057              
1058             sub annotation {
1059 1     1 1 3 my $obj = shift;
1060 1         7 my %parms = @_;
1061 1 50       3 $parms{parent} = $obj if !defined($parms{parent});
1062 1         8 my $anno = Video::CPL::Annotation->new(%parms);
1063 1         5 $obj->addanno($anno);
1064 1         2 return $anno;
1065             }
1066              
1067             =head2 adecoration([annotation parameters])
1068              
1069             =cut
1070              
1071             sub adecoration {
1072 0     0 1 0 my $obj = shift;
1073 0         0 my %parms = @_;
1074 0         0 $parms{clickBehavior} = "javascript";
1075 0 0       0 $parms{parent} = $obj if !defined($parms{parent});
1076 0         0 my $anno = Video::CPL::Annotation->new(%parms);
1077 0         0 $obj->addanno($anno);
1078 0         0 return $anno;
1079             }
1080              
1081             =head2 agoto([annotation parameters])
1082              
1083             adecoration,agoto,ajavascript, and areturnend are shorthand notations to create an annotation and add it
1084             to the current Video::CPL object, setting the parent correctly. Generally they are recommended if the annotation
1085             will be used more than once.
1086              
1087             $anno = $cpl->agoto(balloonText=>"go somewhere",x=>10,y=>10,target=>$somecue);
1088             $cpl->numcue(1)->addanno($anno);
1089              
1090             For a single-use annotation, Annotation::goto may be more convenient.
1091              
1092             $cpl->regular(time=>10)->goto(balloontext=>"go somewhere",x=>10,y=>10);
1093              
1094             This would create a Cue and add an annotation in one statement.
1095              
1096             The annotation parameters are used with normal Annotation constructor; therefore Story parameters such as
1097             picLoc will cause a Story to be automatically generated.
1098              
1099             =cut
1100              
1101             sub agoto {
1102 0     0 1 0 my $obj = shift;
1103 0         0 my %parms = @_;
1104 0         0 $parms{clickBehavior} = "goto";
1105 0 0       0 $parms{tl} = [$parms{dest}] if defined($parms{dest});
1106 0         0 undef($parms{dest});
1107 0 0       0 $parms{parent} = $obj if !defined($parms{parent});
1108 0         0 my $anno = Video::CPL::Annotation->new(%parms);
1109 0         0 $obj->addanno($anno);
1110 0         0 return $anno;
1111             }
1112              
1113             =head2 ajavascript([annotation paramters])
1114              
1115             =cut
1116              
1117             sub ajavascript {
1118 0     0 1 0 my $obj = shift;
1119 0         0 my %parms = @_;
1120 0         0 $parms{clickBehavior} = "javascript";
1121 0 0       0 $parms{parent} = $obj if !defined($parms{parent});
1122 0         0 my $anno = Video::CPL::Annotation->new(%parms);
1123 0         0 $obj->addanno($anno);
1124 0         0 return $anno;
1125             }
1126              
1127             =head2 areturnend()
1128              
1129             my $anno = $cpl->areturnend(balloonText=>"Return please",x=>10,y=>10);
1130             $cpl->numcue(0)->addanno($anno);
1131              
1132             =cut
1133              
1134             sub areturnend {
1135 0     0 1 0 my $obj = shift;
1136 0         0 my %parms = @_;
1137 0         0 $parms{clickBehavior} = "returnEnd";
1138 0 0       0 $parms{parent} = $obj if !defined($parms{parent});
1139 0         0 my $anno = Video::CPL::Annotation->new(%parms);
1140 0         0 $obj->addanno($anno);
1141 0         0 return $anno;
1142             }
1143              
1144             =head2 userChoice()
1145              
1146             =cut
1147              
1148             sub userChoice {
1149 0     0 1 0 my $obj = shift;
1150 0         0 my %parms = @_;
1151 0         0 return $obj->newcue(cueType=>'userChoice',%parms);
1152             }
1153              
1154             =head2 xmlo($xo)
1155              
1156             Add the XML to output this object to the existing XML::Writer object xo. Creation and printing is done outside this routine.
1157              
1158             =cut
1159              
1160             sub xmlo {
1161 1     1 1 2 my $obj = shift;
1162 1         1 my $xo = shift;
1163              
1164 1         2 my %p;
1165 1         3 foreach my $q (@MPFIELDS){
1166 2 50       10 $p{$q} = $obj->{$q} if defined $obj->{$q};
1167             }
1168              
1169 1         5 $xo->startTag("MediaProgram",%p);
1170 1         107 %p = ();
1171             #the qw defines the order of the attributes, if some order is preferred.
1172 1         3 foreach my $q (@PLMFIELDS){
1173 18 100       42 $p{$q} = $obj->{$q} if defined($obj->{$q});
1174             }
1175 1 50       3 if (exists($obj->{video})){
1176 0         0 $xo->startTag("progLevelMetadata",%p);
1177 0         0 $xo->startTag("video");
1178 0         0 my @a = @{$obj->{video}[0]{source}};
  0         0  
1179 0         0 foreach my $ia (0..$#a){
1180 0         0 my %h = %{$a[$ia]};
  0         0  
1181 0         0 $xo->emptyTag("source",%h);
1182             }
1183 0         0 $xo->endTag("video");
1184 0         0 $xo->endTag("progLevelMetadata");
1185             } else {
1186 1         6 $xo->emptyTag("progLevelMetadata",%p);
1187             }
1188              
1189 1         236 foreach my $a (qw(cuePoints webPoints annotations layouts)){
1190 4 100       27 if ($obj->{$a}){
1191 3         5 $xo->startTag($a);
1192 3         55 foreach my $c (@{$obj->{$a}}){
  3         4  
1193 4         194 $c->xmlo($xo);
1194             }
1195 3         41 $xo->endTag($a);
1196             }
1197             }
1198 1         13 $xo->endTag("MediaProgram");
1199             }
1200              
1201             =head2 xml()
1202              
1203             Return the xml format of the current CPL object. This is normally called from print, but can
1204             be called directly.
1205              
1206             =cut
1207              
1208             sub xml {
1209 1     1 1 2 my $obj = shift;
1210 1         2 my $a = "";
1211 1         12 my $xo = new XML::Writer(OUTPUT=>\$a,NEWLINES=>1);
1212 1         201 $obj->xmlo($xo);
1213 1         13 $xo->end();
1214 1         103 return $a;
1215             }
1216              
1217             =head2 print()
1218              
1219             Print out the current xml in the automatically created temporary file within the web-viewable file hiearchy. Use before calling embed.
1220              
1221             =cut
1222              
1223             sub print {
1224 0     0 1   my $obj = shift;
1225 0 0         $obj->fixfile() if !$obj->{filefixed};
1226 0           my $fh = $obj->{fh};
1227 0           print $fh $obj->xml();
1228 0           close $fh;
1229             }
1230              
1231             =head2 embed([height=>yyy,width=>xxx])
1232              
1233             Return the html code used to embed a CPL screen within an html file.
1234              
1235             The height and width parameters are optional, and may be specified as percent or pixels. If not specified, height will be set to 392 pixels and width to 680 pixels.
1236              
1237             CPL parameters that can be specified:
1238             height: Height in pixels or percent. Defaults to frameHeight else videoHeight else 680 pixels.
1239             width: Width in pixels or percent. Defaults to frameWidth else videoWidth else 415 pixels.
1240             player: a URL that reaches the desired Flash player. If not specified, will default to a player
1241             at Coincident TV. This will not be able to access images etc. from a different server unless
1242             there is a file "crossdomain.xml" at the top level of that server. This file should look like
1243              
1244            
1245            
1246            
1247            
1248            
1249            
1250              
1251             media: the URL of the media CTV. If a partial URL, it will be relative to the player.
1252             mergedstyle: if true, include the media as a CGI parameter to the player. This is the norm when using the full CPL experience.
1253              
1254             Additional Adobe parameters:
1255             align: defaults to "middle".
1256             play: defaults to "true".
1257             quality: defaults to "autohigh".
1258             allowfullscreen: defaults to "true".
1259             allowScriptAccess: defaults to "always". In this mode, a webPoint reached from "embed"-ed player
1260             will overwrite the window.
1261             type: defaults to "application/x-shockwave-flash".
1262             pluginspage: defaults to "http://www.adobecom/go/getflashplayer".
1263             bgcolor: Hex value,defaults to "#869ca7".
1264              
1265             =cut
1266              
1267             sub embed {
1268 0     0 1   my $obj = shift;
1269 0           my %parms = @_;
1270 0           my $ret;
1271 0   0       my $media = $parms{media} || $obj->{ctvfilename};
1272 0   0       my $player = $parms{flexplayer} || "http://metabase.coincident.tv/cpan/player/CTVWebPlayerS.swf";
1273 0           my $mergedstyle = $parms{mergedstyle};
1274              
1275 0           my %pr;
1276             #my %pr = $obj->print(%parms);
1277 0 0         $media = $pr{url} if !$parms{media};
1278              
1279             #good values are 680x415 435x276 314x208 or 1000x595
1280             #use parameter else frameWidth else videoWidth else 680 (415)
1281 0           my $width = 680;
1282 0 0         $width = $obj->{videoWidth} if exists($obj->{videoWidth});
1283 0 0         $width = $obj->{frameWidth} if exists($obj->{frameWidth});
1284 0 0         $width = $parms{width} if exists($parms{width});
1285 0           my $height = 415;
1286 0 0         $height = $obj->{videoHeight} if exists($obj->{videoHeight});
1287 0 0         $height = $obj->{frameHeight} if exists($obj->{frameHeight});
1288 0 0         $height = $parms{height} if exists($parms{height});
1289              
1290 0 0 0       $media = $obj->{ctvfilename} if !$media && exists($obj->{ctvfilename});
1291 0 0         confess("No media in embed\n") if !$media;
1292             #print '
1293 0           my %ret;
1294 0 0         $ret{src} = $mergedstyle?"$player?media=$media":$player;
1295 0 0         $ret{flashvars} = "media=".$media if !$mergedstyle;
1296 0           $ret{width} = $width;
1297 0           $ret{height} = $height;
1298 0 0         $ret{align} = exists($parms{align})?$parms{align}:"middle";
1299 0 0         $ret{play} = exists($parms{play})?$parms{play}:"true";
1300 0 0         $ret{quality} = exists($parms{quality})?$parms{quality}:"autohigh";
1301 0 0         $ret{allowfullscreen} = exists($parms{allowfullscreen})?$parms{allowfullscreen}:"true";
1302 0 0         $ret{allowScriptAccess} = exists($parms{allowScriptAccess})?$parms{allowScriptAccess}:"always";
1303 0 0         $ret{type} = exists($parms{type})?$parms{type}:"application/x-shockwave-flash";
1304 0 0         $ret{pluginspage} = exists($parms{pluginspage})?$parms{pluginspage}:"http://www.adobe.com/go/getflashplayer";
1305 0 0         $ret{bgcolor} = exists($parms{bgcolor})?$parms{bgcolor}:"#869ca7";
1306 0           my @ret;
1307 0           foreach my $k (keys %ret){
1308 0           push @ret, "$k=\"$ret{$k}\"";
1309             }
1310 0           return "\n";
1311             }
1312              
1313             =head2 Video::CPL::checkready(local=>$file)
1314              
1315             This is a function, and not a method. Typically it is called before creating a CPL object
1316              
1317             =cut
1318              
1319             sub checkready {
1320 0     0 1   my %p = @_;
1321 0           my $SRC = "http://metabase.coincident.tv/cpan/player/";
1322             #if local, just get the player
1323             #if dir, get everything into said directory
1324 0 0         if ($p{local}){
    0          
1325 0 0 0       return 1 if -r $p{local} && (-s $p{local} > 200000);
1326 0 0         return 1 if getfile("$SRC/CTVWebPlayerS.swf",$p{local},200000);
1327 0           return 0;
1328             } elsif ($p{dir}){
1329 0           my %files = ("CTVWebPlayerS.swf"=>400000,
1330             "defaultSkin.html"=>100,
1331             "index.html"=>2000,
1332             "styles.css"=>500,
1333             "preLoader/CTVPreLoader.swf"=>2000,
1334             "scripts/CTVLayoutFunc.js"=>5000,
1335             "scripts/expressInstall.swf"=>100,
1336             "scripts/jquery-1.3.2.min.js"=>10000,
1337             "scripts/swfobject.js"=>3000,
1338             "ui/CTVCloseButton-over.png"=>100,
1339             "ui/CTVCloseButton.png"=>100);
1340 0 0         make_path($p{dir}) if !-d $p{dir};
1341 0           foreach my $x (keys %files){
1342 0 0         if ($x =~ /(.*)\//){
1343 0           my $prefix = $1;
1344 0 0         make_path("$p{dir}/$prefix") if !-d "$p{dir}/$prefix";
1345             }
1346 0           getfile("$SRC$x","$p{dir}/$x",$files{$x});
1347             }
1348             }
1349             }
1350              
1351             sub getfile {
1352 0     0 0   my $url = shift;
1353 0           my $file = shift;
1354 0           my $minsize = shift;
1355 0           my $fil = get($url);
1356 0 0         if (length($fil) < $minsize){
1357 0           warn("Video::CPL checkready/getfile: tried to fetch ($file) from ($url) but it was shorter than expected ($minsize), failing.\n");
1358 0           return 0;
1359             }
1360 0 0         if (open(FSWF,">$file")){
1361 0           print FSWF $fil;
1362 0           close FSWF;
1363 0           my $actual = -s $file;
1364 0 0         if ($actual != length($fil)){
1365 0           warn("Video::CPL checkready/getfile: fetched ($file) from ($url) of length ".length($fil)." but only wrote $actual bytes, failing.\n");
1366 0           return 0;
1367             }
1368 0           return 1;
1369             } else {
1370 0           warn("Video::CPL checkready/getfile: fetched from ($url), could not write to ($file), failing.\n");
1371 0           return 0;
1372             }
1373             }
1374              
1375             =head2 isyoutube($url)
1376              
1377             Return true if this URL appears to be a Youtube video.
1378              
1379             =cut
1380              
1381             sub isyoutube {
1382             #utility routine, not an object method
1383 0     0 1   my $x = shift;
1384 0 0         return undef if $x !~ /http:\/\/www.youtube.com\/watch\?v=([A-Za-z0-9\-\_]{11})/;
1385 0           my $code = $1;
1386 0           return youtubefromcode($code);
1387             }
1388              
1389             =head1 AUTHOR
1390              
1391             Carl Rosenberg, C<< >>
1392              
1393             =head1 BUGS
1394              
1395             Please report any bugs to Coincident TV.
1396              
1397             =head1 SUPPORT
1398              
1399             You can find documentation for this module with the perldoc command.
1400              
1401             perldoc CPL
1402              
1403             =head1 ACKNOWLEDGEMENTS
1404              
1405             This is actually just a straightforward interface to the work done by the rest of the Coincident team.
1406              
1407             =head1 LICENSE AND COPYRIGHT
1408              
1409             Copyright 2010 Coincident TV
1410              
1411             Licensed under the Apache License, Version 2.0 (the "License");
1412             you may not use this file except in compliance with the License.
1413             You may obtain a copy of the License at
1414              
1415             http://www.apache.org/licenses/LICENSE-2.0
1416              
1417             Unless required by applicable law or agreed to in writing, software
1418             distributed under the License is distributed on an "AS IS" BASIS,
1419             WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1420             See the License for the specific language governing permissions and
1421             limitations under the License.
1422              
1423             =cut
1424              
1425             1; # End of CPL