File Coverage

blib/lib/WWW/Video/Streamer.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package WWW::Video::Streamer;
2              
3 1     1   22287 use warnings;
  1         3  
  1         34  
4 1     1   5 use strict;
  1         3  
  1         34  
5 1     1   974 use Config::IniHash;
  0            
  0            
6             use Text::NeatTemplate;
7             use String::ShellQuote;
8             use CGI qw/:standard/;
9             use File::MimeInfo;
10             use Cwd 'abs_path';
11              
12             $CGI::POST_MAX=1024;
13             $CGI::DISABLE_UPLOADS=1;
14              
15             =head1 NAME
16              
17             WWW::Video::Streamer - A HTTP video streamer and browser.
18              
19             =head1 VERSION
20              
21             Version 0.0.1
22              
23             =cut
24              
25             our $VERSION = '0.0.1';
26              
27             =head1 SYNOPSIS
28              
29             use WWW::Video::Streamer;
30              
31             my $wvs = WWW::Video::Streamer->new();
32              
33             #read the config if it exists
34             if (-e './config.ini') {
35             $wvs->config('./config.ini');
36             }
37              
38             #invoke the CGI stiff
39             $wvs->cgi;
40              
41             =head1 METHODS
42              
43             =head2 new
44              
45             Initiates the object.
46              
47             my $wvs=WWW::Video::Streamer->new;
48              
49             =cut
50              
51             sub new {
52              
53             my $self={error=>undef, errorString=>'', hi=>5, minL=>5,
54             maxL=>20, replaceP=>50, maxR=>20};
55             bless $self;
56              
57             $self->{mt}='/usr/local/bin/mencoder {$file} -oac mp3lame -ovc lavc -of avi -lavcopts vbitrate={$vb} '.
58             '-lameopts cbr={$ab} {$size} -vf scale={$x}:{$y} -really-quiet -o -';
59             $self->{x}='140';
60             $self->{y}='100';
61             $self->{vb}='120';
62             $self->{ab}='40';
63             $self->{dir}='/arc/video/';
64              
65             #this will be used regardless in CGI more
66             #also if being used as a deamon, this saves from doing it more than once
67             $self->{cgi}=CGI->new;
68              
69             return $self;
70             }
71              
72             =head2 cgi
73              
74             This handles a handles the CGI interface.
75              
76             $wvs->cgi;
77              
78             =cut
79              
80             sub cgi{
81             my $self=$_[0];
82              
83             #get the file in question and convert it to absolute path
84             my $file=$self->{cgi}->param('file');
85             if (!defined($file)) {
86             $file='';
87             }
88             my $path=$self->{dir}.'/'.$file;
89             $path=abs_path($path);
90             if ($path eq '') {
91             $path=$self->{dir}.'/';
92             }
93              
94             #make sure the file in question is below the directory in question
95             my $dirtest=$self->{dir}; #this is done just incase the directory that the user specified does not end in a /
96             $dirtest=abs_path($dirtest);
97             #if this is blank, it means it does not exist
98             if ($dirtest eq '') {
99             $self->{error}=11;
100             $self->{errorString}='"'.$self->{dir}.'" does not exist';
101             warn('WWW-Video-Streamer cgi:11: '.$self->{errorString});
102             print $self->{cgi}->header(-status=>'404 file not found');
103             return undef;
104             }
105             $dirtest=$dirtest.'/';
106             my $regex='^'.quotemeta($dirtest);
107             if ($path !~ /$regex/) {
108             warn('WWW-Video-Streamer cgi: The path in question is out of the base directory. Using the default.');
109             $path=$self->{dir};
110             $path=abs_path($path).'/';
111             }
112              
113             if (!-e $path) {
114             $self->{error}=10;
115             $self->{errorString}='The requested path, "'.$path.'", does not exist. file="'.$file.'"';
116             warn('WWW-Video-Streamer cgi:10: '.$self->{errorString});
117             print $self->{cgi}->header(-status=>'404 file not found');
118             return undef;
119             }
120              
121             #handles it if it is a file
122             if (-d $path) {
123             $self->dir($path);
124             return 1;
125             if ($self->{error}) {
126             warn('WWW-Video-Streamer cgi: $self->dir("'.$path.'") failed');
127             }
128             }
129              
130             #handles it if it is a file
131             if (-f $path) {
132             my $px=$self->{cgi}->param('x');
133             my $py=$self->{cgi}->param('y');
134             my $pab=$self->{cgi}->param('ab');
135             my $pvb=$self->{cgi}->param('vb');
136              
137             #make sure px is numeric
138             my $test=$px;
139             if (defined($px)) {
140             $test=~s/[0123456789]//g;
141             if ($test eq '') {
142             $self->{x}=$px;
143             }
144             }
145              
146             #make sure py is numeric
147             $test=$py;
148             if (defined($py)) {
149             $test=~s/[0123456789]//g;
150             if ($test eq '') {
151             $self->{y}=$py;
152             }
153             }
154              
155             #make sure pab is numeric
156             $test=$pab;
157             if (defined($pab)) {
158             $test=~s/[0123456789]//g;
159             if ($test eq '') {
160             $self->{ab}=$pab;
161             }
162             }
163              
164             #make sure pvb is numeric
165             $test=$pvb;
166             if (defined($px)) {
167             $test=~s/[0123456789]//g;
168             if ($test eq '') {
169             $self->{vb}=$pvb;
170             }
171             }
172              
173             $self->stream($path);
174             if ($self->{error}) {
175             warn('WWW-Video-Streamer cgi: $self->stream("'.$path.'") failed');
176             }
177             return 1;
178             }
179              
180             #if we get here, it is not a file or directory... error
181             print $self->{cgi}->header(-status=>'404 file not found');
182             $self->{error}=3;
183             $self->{errorString}='The file requested "'.$path.'" below "'.$self->{dir}.'" is not a file or directory';
184             warn('WWW-Video-Streamer cgi:4: '.$self->{errorString});
185             return undef;
186             }
187              
188             =head2 config
189              
190             This reads the config.
191              
192             if (-e './config.ini') {
193             $wvs->config('./config.ini');
194             if($wvs->{error}){
195             print "Error!\n";
196             }
197             }
198              
199             =cut
200              
201             sub config{
202             my $self=$_[0];
203             my $file=$_[1];
204              
205             if (!defined($file)) {
206             $file='./config.ini';
207             }
208              
209             my $ini=ReadINI($file);
210             if (!defined($ini)) {
211             $self->{errorString}='Failed to read "'.$file.'"';
212             $self->{error}=1;
213             warn('WWW-Video-Streamer config:1: '.$self->{errorString});
214             return undef;
215             }
216              
217             #check if the various internal definable stuff is present in the config and if it is
218             #copy it into self
219              
220             if (defined($ini->{mt})) {
221             $self->{mt}=$ini->{mt};
222             }
223              
224             if (defined($ini->{x})) {
225             $self->{x}=$ini->{x};
226             }
227              
228             if (defined($ini->{y})) {
229             $self->{y}=$ini->{y};
230             }
231              
232             if (defined($ini->{vb})) {
233             $self->{vb}=$ini->{vb};
234             }
235              
236             if (defined($ini->{ab})) {
237             $self->{ab}=$ini->{ab};
238             }
239              
240             if (defined($ini->{dir})) {
241             $self->{dir}=$ini->{dir};
242             }
243              
244             return 1;
245             }
246              
247             =head2 dir
248              
249             This handles displaying directories.
250              
251            
252              
253             =cut
254              
255             sub dir{
256             my $self=$_[0];
257             my $path=$_[1];
258              
259             #make sure the path is defined
260             if (!defined($path)) {
261             $self->{error}=7;
262             $self->{errorString}='No path defined';
263             warn('WWW-Video-Streamer dir:7: '.$self->{errorString});
264             return undef;
265             }
266              
267             #make sure it exists
268             if (!-e $path) {
269             $self->{error}=6;
270             $self->{errorString}='The path "'.$path.'" does not exist';
271             warn('WWW-Video-Streamer dir:6: '.$self->{errorString});
272             return undef;
273             }
274              
275             #make sure it exists
276             if (!-d $path) {
277             $self->{error}=8;
278             $self->{errorString}='The path "'.$path.'" does not a directory';
279             warn('WWW-Video-Streamer dir:8: '.$self->{errorString});
280             return undef;
281             }
282              
283             #open the directory and read it
284             my @entries;
285             if (opendir(DIR, $path)) {
286             @entries=readdir(DIR);
287             closedir(DIR);
288             }else {
289             print $self->{cgi}->header(-status=>'404 file not found');
290             print 'Error:404: File not found.';
291             $self->{error}=5;
292             $self->{errorString}='opendir(DIR, "'.$path.'") failed';
293             warn('WWW-Video-Streamer dir:5: '.$self->{errorString});
294             return undef;
295             }
296              
297             @entries=sort(@entries);
298              
299             #we ignore dot files as these are most likely not useful
300             @entries=grep(!/^\./, @entries);
301              
302             #holds directories in this directory
303             my @dirs;
304              
305             #holds the files in this directory
306             my @files;
307              
308             #break them all apart
309             my $int=0;
310             while (defined($entries[$int])) {
311             #add it if it is a directory
312             if (-d $path.'/'.$entries[$int]) {
313             push(@dirs, $entries[$int]);
314             }
315              
316             #add it if it is a file
317             if (-f $path.'/'.$entries[$int]) {
318             #check if it is playable and adds it to the list of files
319             my $playable=$self->playable($entries[$int]);
320             if ($playable) {
321             push(@files, $entries[$int]);
322             }
323             }
324              
325             $int++;
326             }
327              
328             #get the required parameters
329             my $ab=$self->{cgi}->param('ab');
330             my $vb=$self->{cgi}->param('vb');
331             my $x=$self->{cgi}->param('x');
332             my $y=$self->{cgi}->param('y');
333              
334             #makes sure all of the required values are defined
335             if (!defined($ab)) {
336             $ab=$self->{ab};
337             }
338             if (!defined($vb)) {
339             $vb=$self->{vb};
340             }
341             if (!defined($x)) {
342             $x=$self->{x};
343             }
344             if (!defined($y)) {
345             $y=$self->{y};
346             }
347              
348             #make sure all the values are numeric
349            
350              
351             #this is the directory that will be displayed
352             my $displaydir=$path;
353             my $bd=$self->{dir}.'/'; #get the base path and tack on a /
354             $bd=File::Spec->rel2abs($bd);#cleanup the base path
355             $displaydir=~s/^$bd//; #remove the base path from the path
356             if ($displaydir eq '') {
357             $displaydir='/';
358             }
359              
360             # my $url='http://'.$ENV{HTTP_HOST}.$ENV{SCRIPT_NAME}.'?ab='.$ab.'+vb='.$vb.'+x='.$x.'+y='.$y;
361             my $url=$ENV{SCRIPT_NAME}.'?ab='.$ab.'&vb='.$vb.'&x='.$x.'&y='.$y.'&file=';
362              
363             print $self->{cgi}->header(-type=>'text/html');
364              
365             #prints the head of the the html
366             print ''."\n".
367             ''."\n".
368             ''."\n".
369             ' '."\n".
370             ' WWW::Video::Streamer: '.$displaydir.' '."\n".
371             ' '."\n".
372             ' '."\n".
373             '
'."\n".
374             ' ab='."\n".
375             ' vb='."\n".
376             ' x='."\n".
377             ' y='."\n".
378             ' '."\n".
379             ' '."\n".
380             '
'."\n".
381             ' directory: '.$displaydir.'
'."\n".
382             ' '."\n". '."\n". '."\n". '."\n"; '."\n". '."\n". '."\n"; '."\n". '."\n". '."\n";
383             '
384             ' '."\n".
385             ' ..
'."\n".
386             '
387             '
388              
389             #this presents the the directories to the users
390             $int=0;
391             while ($dirs[$int]) {
392             my $directory=$displaydir.'/'.$dirs[$int];
393             $directory=~s/\/\//\//g;
394             print '
395             ' '."\n".
396             ' '.$dirs[$int].'
'."\n".
397             '
398             '
399              
400             $int++;
401             }
402              
403             #this presents the the directories to the users
404             $int=0;
405             while ($files[$int]) {
406             my $file=$displaydir.'/'.$files[$int];
407             $file=~s/\/\//\//g;
408             print '
409             ' '."\n".
410             ' '.$files[$int].'
'."\n".
411             '
412             '
413              
414             $int++;
415             }
416              
417              
418             #this prints the end of it
419             print '
'."\n".
420             ' '."\n".
421             ''."\n";
422              
423             return 1;
424             }
425              
426             =head2 mencoder
427              
428             Takes the template and generates a proper string to run mencoder.
429              
430             my $mencoderstring=$wvs->mencoder($file);
431             if(!$wvc->{error}){
432             print "Error!\n";
433             }
434              
435             =cut
436              
437             sub mencoder{
438             my $self=$_[0];
439             my $file=$_[1];
440              
441             if (!defined($file)) {
442             $self->{errorString}='No file specified.';
443             $self->{error}=2;
444             warn('WWW-Video-Streamer stream:2: '.$self->{errorString});
445             return undef;
446             }
447              
448             #escape any bad characters
449             $file=shell_quote($file);
450              
451             my $tobj = Text::NeatTemplate->new();
452              
453             #initiates the object that will
454             my %data;
455             $data{x}=$self->{x};
456             $data{y}=$self->{y};
457             $data{ab}=$self->{ab};
458             $data{vb}=$self->{vb};
459             $data{file}=$file;
460              
461             my $mencoder=$tobj->fill_in(data_hash=>\%data,
462             template=>$self->{mt});
463              
464             return $mencoder
465             }
466              
467             =head2 playable
468              
469             This checks if the file is playable or not.
470              
471             Currently this just checks if the mimetype matches /^video\//.
472              
473             This will error if no file is specified.
474              
475             $playable=$wvc->playable($file);
476             if($wvc->{error}){
477             print "Error!\n";
478             }else{
479             if($playable){
480             print "Playable.\n";
481             }else{
482             print "Not playable.\n";
483             }
484             }
485              
486             =cut
487              
488             sub playable{
489             my $self=$_[0];
490             my $file=$_[1];
491              
492             #get the type
493             my $type=mimetype($file);
494             if (!defined($type)) {
495             return undef;
496             }
497              
498             #make sure it matches the video type
499             if ($type=~/^video\//) {
500             return 1;
501             }
502              
503             return undef;
504             }
505              
506             =head2 stream
507              
508             This streams the specified file to standard out.
509              
510             $wvc->stream($file);
511             if(!$wvc->{error}){
512             print "Error!\n";
513             }
514              
515             =cut
516              
517             sub stream{
518             my $self=$_[0];
519             my $file=$_[1];
520              
521             if (!defined($file)) {
522             $self->{errorString}='No file specified.';
523             $self->{error}=2;
524             warn('WWW-Video-Streamer stream:2: '.$self->{errorString});
525             return undef;
526             }
527              
528             my $mencoder=$self->mencoder($file);
529             if($self->{error}){
530             warn('WWW-Video-Streamer stream: $self->mencoder("'.$file.'")');
531             return undef;
532             }
533              
534             warn($mencoder);
535              
536             print $self->{cgi}->header(-type => "video/avi");
537              
538             system($mencoder);
539              
540             return 1;
541             }
542              
543             =head2 errorblank
544              
545             This blanks the error storage and is only meant for internal usage.
546              
547             It does the following.
548              
549             $self->{error}=undef;
550             $self->{errorString}="";
551              
552             =cut
553              
554             sub errorblank{
555             my $self=$_[0];
556              
557             $self->{error}=undef;
558             $self->{errorString}="";
559              
560             return 1;
561             }
562              
563             =head1 ERROR CODES
564              
565             =head2 1
566              
567             Failed to read the config.
568              
569             =head2 2
570              
571             No file is defined.
572              
573             =head2 3
574              
575             File requested is not below the specified directory.
576              
577             =head2 4
578              
579             The file is below the specified directory, but is not a file or directory.
580              
581             =head2 5
582              
583             Opendir failed for the path.
584              
585             =head2 6
586              
587             Path does not exist.
588              
589             =head2 7
590              
591             Path is not defined.
592              
593             =head2 8
594              
595             Path is not a directory.
596              
597             =head2 9
598              
599             Failed to build mencoder string.
600              
601             =head2 10
602              
603             The requested file does not exist.
604              
605             =head2 11
606              
607             The video directory does not exist.
608              
609             =head1 CONFIG FILE
610              
611             The below is a example config file at the defaults.
612              
613             x=100
614             y=100
615             vb=120
616             ab=40
617             dir=/arc/video/
618             mt=/usr/local/bin/mencoder {$file} -oac mp3lame -ovc lavc -of avi -lavcopts vbitrate={$vb} -lameopts cbr={$ab} {$size} -vf scale={$x}:{$y} -really-quiet -o -
619              
620             =head2 ab
621              
622             This is the default audio bit rate to use for the encoding.
623              
624             =head2 dir
625              
626             This is the base directory for video.
627              
628             =head2 mt
629              
630             This is the mencoder template that will be used.
631              
632             =head3 {$ab}
633              
634             This part of the template will be replaced with the audio bit rate.
635              
636             =head3 {$file}
637              
638             This part of template will be replaced with the file name.
639              
640             =head3 {$vb}
641              
642             This part of the template will be replaced with the video bit rate.
643              
644             =head3 {$x}
645              
646             This part of the template will be replaced with the video width.
647              
648             =head3 {$y}
649              
650             This part of the template will be replaced with the video heigth.
651              
652             =head2 x
653              
654             This is the default video width
655              
656             =head2 y
657              
658             This is the default video hieght.
659              
660             =head1 DOT FILES
661              
662             These are currently ignored by the dir function.
663              
664             =head1 SECURITY
665              
666             The file names passed to it are escaped when they are passed to mplayer.
667              
668             Care should be taken to make sure that the config file is not writable by any untrusted users
669             as changing the 'mt' variable can allow other things to be executed.
670              
671             If none-numeric values for 'x', 'y', 'ab', or 'vb' are found when it goes to play it,
672             the defaults are used.
673              
674             =head1 USING
675              
676             Copy 'bin/wvs.cgi' to your directory on your web server, enable CGI on that directory,
677             and then if you want to override the defaults create 'config.ini' in hat directory.
678              
679             =head1 AUTHOR
680              
681             Zane C. Bowers, C<< >>
682              
683             =head1 BUGS
684              
685             Please report any bugs or feature requests to C, or through
686             the web interface at L. I will be notified, and then you'll
687             automatically be notified of progress on your bug as I make changes.
688              
689              
690              
691              
692             =head1 SUPPORT
693              
694             You can find documentation for this module with the perldoc command.
695              
696             perldoc WWW::Video::Streamer
697              
698              
699             You can also look for information at:
700              
701             =over 4
702              
703             =item * RT: CPAN's request tracker
704              
705             L
706              
707             =item * AnnoCPAN: Annotated CPAN documentation
708              
709             L
710              
711             =item * CPAN Ratings
712              
713             L
714              
715             =item * Search CPAN
716              
717             L
718              
719             =back
720              
721              
722             =head1 ACKNOWLEDGEMENTS
723              
724              
725             =head1 COPYRIGHT & LICENSE
726              
727             Copyright 2009 Zane C. Bowers, all rights reserved.
728              
729             This program is free software; you can redistribute it and/or modify it
730             under the same terms as Perl itself.
731              
732              
733             =cut
734              
735             1; # End of WWW::Video::Streamer