1 # @(#)$Header: /home/mythtv/mythtvrep/scripts/nv_perl_libs/mnvcommonsubs.pm,v 1.10 2010/07/25 00:11:35 mythtv Exp $
2 # Auric 2010/01/10 http://web.aanet.com.au/auric/
4 # MythNetvision Grabber Script utility subs
6 #################################### Util Subs ############################################
18 # mythbackend sets HOME and MYTHCONFDIR to /var/lib/mythtv.
19 # So have to hack it back to the real home for require MythTV to work.
20 $ENV{'HOME'} = (getpwuid $>)[7];
21 delete $ENV{'MYTHCONFDIR'};
26 our(@ISA, @EXPORT, @EXPORT_OK, $VERSION);
27 $VERSION = '$Revision: 1.10 $'; $VERSION =~ s/\D*([\d\.]+)\D*/$1/; # rcs tag populated
31 @EXPORT = qw(mnvURLEncode mnvprintversion mnvcleanexit mnvinfomsg mnvcleantext mnvnumresults mnvfilter
32 mnvprinttree mnvprintsearch mnvrssitem2mnv mnvistype mnvgetsetting mnvgeticonDir mnvrssheader mnvrssfooter
33 mnvgetflashplayerurl mnvloadconfig mnvgetconfig mnvsetconfig);
34 @EXPORT_OK = qw(); # symbols to export on request
37 eval { $mythobj = MythTV->new(); };
38 ($@) and die("Could not create MythTV-new() $@");
40 # Netvision version access as $mnvcommonsubs::netvisionver
41 our $netvisionver = mnvgetnetvisionver();
43 # Get a mnv configuration
44 # Default config values. These can be overridded by mnvsetconfig
52 # A network player like a flash or html5 html. TODO 0.24 May not be approved
54 'netplayertype' => "",
55 # Seconds to cache results
57 'defaultautoplay' => 'autostart=1&autoplay=1'
63 return $config{$value};
66 # Set a mnv configuration
71 if (ref($config{$value}) eq "ARRAY") {
72 push(@{$config{$value}}, $data);
74 $config{$value} = $data;
78 # Load a mnv configuration file
79 # $ENV{'HOME'}/.mythtv/MythNetvision/userGrabberPrefs/${configfile}.cfg
83 # feedurl+url1 (Creates a array feedurl of all the entries)
91 my $configfile = shift @_;
92 my $section = shift @_;
94 my $cfile = "/$ENV{'HOME'}/.mythtv/MythNetvision/userGrabberPrefs/${configfile}.cfg";
95 my $ss = stat($cfile);
97 open(CF, "<$cfile") or warn "Could not load $cfile";
98 my $in = 1; # In default section
103 if ($in == 1 && /=/) {
104 my ($value, @data) = split('=', $_);
105 $config{$value} = join('=', @data);
109 if ($in == 1 && /\+/) {
110 my ($value, @data) = split('\+', $_);
111 push(@{$config{$value}}, join('+', @data));
114 if (/^\s*\[(.*)\]\s*$/) {
115 if ($1 eq $section) {
126 # Get a mythdb setting
128 my $setting = shift @_;
129 my $hostname = shift @_;
133 $data = $mythobj->backend_setting($setting, $hostname);
135 $data = $mythobj->backend_setting($setting);
141 # Find netvision version
142 sub mnvgetnetvisionver {
144 my $netvisionver = "NA";
145 my $printerror = $mythobj->{'dbh'}->{'PrintError'};
146 my $printwarn = $mythobj->{'dbh'}->{'PrintWarn'};
147 $mythobj->{'dbh'}->{'PrintError'} = 0;
148 $mythobj->{'dbh'}->{'PrintWarn'} = 0;
149 eval { $mythobj->{'dbh'}->do("select count(*) from internetcontent where 1=0") };
150 unless ($mythobj->{'dbh'}->err) {
151 $netvisionver = "24";
153 eval { $mythobj->{'dbh'}->do("select count(*) from netvisiontreegrabbers where 1=0") };
154 unless ($mythobj->{'dbh'}->err) {
155 $netvisionver = "23";
158 $mythobj->{'dbh'}->{'PrintError'} = $printerror;
159 $mythobj->{'dbh'}->{'PrintWarn'} = $printwarn;
161 return $netvisionver;
164 # Find netvision icon directory
167 # 0.23 mythnetvision.iconDir. 0.24 %SHAREDIR%
168 my $icondir = mnvgetsetting('mythnetvision.iconDir', hostname());
169 ($icondir) and return $icondir;
170 return "%SHAREDIR%/mythnetvision/icons";
175 my $theURL = shift @_;
177 $theURL =~ s/([\W])/"%" . uc(sprintf("%2.2x",ord($1)))/eg;
181 # print or return a typical <rss...> header
183 # in list context, return a list of lines with the header in it
184 # in scalar context, return as a string
185 # in void context, print the header to stdout
189 my $header='<?xml version="1.0" encoding="UTF-8"?>
191 xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
192 xmlns:content="http://purl.org/rss/1.0/modules/content/"
193 xmlns:cnettv="http://cnettv.com/mrss/"
194 xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule"
195 xmlns:media="http://search.yahoo.com/mrss/"
196 xmlns:atom="http://www.w3.org/2005/Atom"
197 xmlns:amp="http://www.adobe.com/amp/1.0"
198 xmlns:dc="http://purl.org/dc/elements/1.1/"
199 xmlns:mythtv="http://www.mythtv.org/wiki/MythNetvision_Grabber_Script_Format">
203 return split(/\n/,$header);
204 } elsif(defined wantarray()) {
211 # print or return a typical </rss> footer to close out an rss block
213 # in list context, return a list of lines with the footer in it
214 # in scalar context, return as a string
215 # in void context, print the header to stdout
218 my $footer="</rss>\n";
220 return split(/\n/,$footer);
221 } elsif(defined wantarray()) {
228 sub mnvprintversion {
230 my $command = shift @_;
231 my $author = shift @_;
232 my $commandthumbnail = shift @_;
233 my $version = shift @_;
234 my $description = shift @_;
236 print '<grabber>'."\n";
237 print ' <name>'.$site.'</name>'."\n";
238 print ' <command>'.$command.'</command>'."\n";
239 print ' <author>'.$author.'</author>'."\n";
240 print ' <thumbnail>'.$commandthumbnail.'</thumbnail>'."\n";
241 print ' <type>video</type>'."\n";
242 print ' <description>'.$description.'</description>'."\n";
243 print ' <version>'.$version.'</version>'."\n";
244 print ' <search>true</search>'."\n";
245 print ' <tree>true</tree>'."\n";
246 print '</grabber>'."\n";
249 # Download a icon from url
253 my $icondir = shift @_;
255 # Only works for 0.23
256 #my $icondir = mnvgeticonDir();
258 my $ss = stat("${icondir}/${name}");
259 ($ss) and return "${icondir}/${name}";
261 my @imgexts = ('jpg', 'jpeg', 'png', 'gif');
262 my $nameext = (fileparse($name, @imgexts))[2];
263 my $urlext = (fileparse($url, @imgexts))[2];
265 mnvinfomsg(1, "Getting $url");
266 my $content = get($url);
268 die "Could not retrieve $url";
271 open(IC, ">${icondir}/.tmp") or return 0;
275 if ($urlext eq $nameext) {
276 copy("${icondir}/.tmp", "${icondir}/${name}");
278 my $image=Image::Magick->new;
279 $image->Read(filename=>"${icondir}/.tmp");
280 $image->Write(filename=>"${icondir}/${name}");
282 unlink("${icondir}/.tmp");
284 mnvinfomsg(1, "Saved icon ${icondir}/${name}");
285 return "${icondir}/${name}";
291 fileno(FH) and close(FH);
293 # called by signalhandler
301 # $config{'mnvinfo'} specifies level of messages to print
302 # $config{'mnvinfoop'} specifies o/p file
304 my $level = shift @_;
307 ($config{'mnvinfo'} < $level) and return;
308 $mesg = encode('utf8', $mesg);
309 if ($config{'mnvinfoop'} =~ /\D/) {
310 open(FH, ">$config{'mnvinfoop'}") unless fileno(FH);
312 print FH "$t $level $mesg\n";
314 print STDERR "$mesg\n";
318 # Clean and encode a string
324 $text =~ s/^\s+|\s+$//g;
325 $text = encode_entities($text);
329 # Number of items found
331 my $diritemsref = shift @_;
334 foreach my $dir (keys(%$diritemsref)) {
335 (@{$diritemsref->{$dir}}) or next;
336 foreach my $item (@{$diritemsref->{$dir}}) {
337 ($item) or next; # somewhere I get null references.
344 # filter the items hash
345 # input hash ref produced by builditems
346 # output number of items
348 my $diritemsref = shift @_;
349 my $search = shift @_;
351 my $filtereditems = 0;
353 foreach my $dir (keys(%$diritemsref)) {
354 for (my $c = 0; $c <= $#{$diritemsref->{$dir}}; $c++) {
355 if (${$diritemsref->{$dir}}[$c]->{'title'} =~ /$search/i || ${$diritemsref->{$dir}}[$c]->{'author'} =~ /$search/i ||
356 ${$diritemsref->{$dir}}[$c]->{'description'} =~ /$search/i || ${$diritemsref->{$dir}}[$c]->{'link'} =~ /$search/i) {
358 mnvinfomsg(2, "Saved $dir ".${$diritemsref->{$dir}}[$c]->{'title'});
360 mnvinfomsg(2, "Deleted $dir ".${$diritemsref->{$dir}}[$c]->{'title'});
361 delete(${$diritemsref->{$dir}}[$c]);
365 return $filtereditems;
372 print " ".'<item>'."\n";
373 print " ".'<title>'.$item->{'title'}.'</title>'."\n";
374 print " ".'<mythtv:subtitle>'.$item->{'mythtv:subtitle'}.'</mythtv:subtitle>'."\n";
375 print " ".'<author>'.$item->{'author'}.'</author>'."\n";
376 print " ".'<pubDate>'.$item->{'pubDate'}.'</pubDate>'."\n";
377 print " ".'<description>'.$item->{'description'}.'</description>'."\n";
378 print " ".'<link>'.$item->{'link'}.'</link>'."\n";
379 print " ".'<player>'.$item->{'player'}.'</player>'."\n";
380 print " ".'<playerargs>'.$item->{'playerargs'}.'</playerargs>'."\n";
381 print " ".'<download>'.$item->{'download'}.'</download>'."\n";
382 print " ".'<downloadargs>'.$item->{'downloadargs'}.'</downloadargs>'."\n";
383 print " ".'<media:group>'."\n";
384 print " ".'<media:thumbnail url="'.$item->{'media:thumbnailurl'}.'"/>'."\n";
385 print " ".'<media:content url="'.$item->{'media:contenturl'}.'" length="'.$item->{'media:contentlength'}.'" duration="'.$item->{'media:contentduration'}.'" width="'.$item->{'media:contentwidth'}.'" height="'.$item->{'media:contentheight'}.'" lang="'.$item->{'media:contentlanguage'}.'"/>'."\n";
386 print " ".'</media:group>'."\n";
387 print " ".'<rating>'.$item->{'rating'}.'</rating>'."\n";
388 print " ".'<mythtv:country>'.$item->{'mythtv:country'}.'</mythtv:country>'."\n";
389 print " ".'<mythtv:season>'.$item->{'mythtv:season'}.'</mythtv:season>'."\n";
390 print " ".'<mythtv:episode>'.$item->{'mythtv:episode'}.'</mythtv:episode>'."\n";
391 print " ".'<mythtv:customhtml>'.$item->{'mythtv:customhtml'}.'</mythtv:customhtml>'."\n";
392 print " ".'</item>'."\n";
397 # input hash ref produced by builditems
399 my $diritemsref = shift @_;
400 my $mostrecentnumber = shift @_;
402 my $icondir = mnvgeticonDir();
404 foreach my $dir (sort(keys(%$diritemsref))) {
405 (@{$diritemsref->{$dir}}) or next;
407 if ($mostrecentnumber) {
409 foreach my $item (@{$diritemsref->{$dir}}) {
410 ($item) or next; # somewhere I get null references.
411 if ($dirprinted == 0) {
413 # Need it here as don't have $item outside loop.
414 print " ".'<directory name="'.mnvcleantext($dir).'" thumbnail="'.$item->{'dirthumbnail'}.'">'."\n";
415 print " ".'<directory name="Most Recent" thumbnail="'.$icondir.'/directories/topics/most_recent.png">'."\n";
419 ($printcount >= $mostrecentnumber) and last;
421 print " ".'</directory>'."\n";
423 foreach my $item (@{$diritemsref->{$dir}}) {
424 ($item) or next; # somewhere I get null references.
425 if ($dirprinted == 0) {
427 # Need it here as don't have $item outside loop.
428 print " ".'<directory name="'.mnvcleantext($dir).'" thumbnail="'.$item->{'dirthumbnail'}.'">'."\n";
432 print " ".'</directory>'."\n";
437 # input hash ref produced by builditems
439 my $diritemsref = shift @_;
442 my $numresults = mnvnumresults($diritemsref);
443 if ($numresults < 1) {
444 print " ".'<numresults>0</numresults>'."\n";
445 print " ".'<returned>0</returned>'."\n";
446 print " ".'<startindex>0</startindex>'."\n";
451 $returned = $numresults - (($page - 1) * 20);
453 $returned = $numresults;
455 ($returned > 20) and $returned = 20;
456 ($returned < 0) and $returned = 0;
457 my $startindex = ($page * 20) + 1;
458 ($startindex >= $numresults) and $startindex = 0;
460 print " ".'<numresults>'.$numresults.'</numresults>'."\n";
461 print " ".'<returned>'.$returned.'</returned>'."\n";
462 print " ".'<startindex>'.$startindex.'</startindex>'."\n";
464 my $scount = 0; my $ecount = 0;
465 L1: foreach my $dir (sort(keys(%$diritemsref))) {
466 (@{$diritemsref->{$dir}}) or next;
467 foreach my $item (@{$diritemsref->{$dir}}) {
468 ($item) or next; # somewhere I get null references.
469 if ($page > 1 && $scount < (($page - 1) * 20)) {
475 ($ecount >= $returned) and last L1;
482 # input rss->parse item
483 # output ref to %mnvitem
502 #print Dumper($item);
504 ($item->{'title'}) or return 0;
505 $mnvitem{'title'} = mnvcleantext($item->{'title'});
507 ($item->{'link'}) and $mnvitem{'weburl'} = mnvcleantext($item->{'link'});
508 ($item->{'enclosure'}->{'url'}) and $mnvitem{'contenturl'} = mnvcleantext($item->{'enclosure'}->{'url'});
509 (($mnvitem{'weburl'}) || ($mnvitem{'contenturl'})) or return 0;
510 ($mnvitem{'contenturl'}) or $mnvitem{'contenturl'} = $mnvitem{'weburl'};
512 if ($item->{'itunes'}->{'author'}) {
513 $mnvitem{'author'} = mnvcleantext($item->{'itunes'}->{'author'});
514 } elsif ($item->{'dc'}->{'creator'}) {
515 $mnvitem{'author'} = mnvcleantext($item->{'dc'}->{'creator'});
516 } elsif ($item->{'author'}) {
517 $mnvitem{'author'} = mnvcleantext($item->{'author'});
520 ($item->{'pubDate'}) and $mnvitem{'pubDate'} = mnvcleantext($item->{'pubDate'});
522 $mnvitem{'thumbnail'} = $icon;
523 if ($item->{'thumbnail'}) {
524 $mnvitem{'thumbnail'} = mnvcleantext($item->{'thumbnail'});
525 } elsif ($item->{'http://blip.tv/dtd/blip/1.0'}->{'smallThumbnail'}) {
526 $mnvitem{'thumbnail'} = mnvcleantext($item->{'http://blip.tv/dtd/blip/1.0'}->{'smallThumbnail'});
527 } elsif ($item->{'media'}->{'thumbnail'}) {
528 # To get this need to hack xml before $rss->parse with following
529 # $content =~ s/<media:thumbnail\s+url=\"([^\"]*)\"\s*\/>/<media:thumbnail>$1<\/media:thumbnail>/g;
530 $mnvitem{'thumbnail'} = mnvcleantext($item->{'media'}->{'thumbnail'});
531 } elsif ($item->{'description'}) {
532 # Sometimes hide thumbernail in description.
533 foreach my $line (split('\n', $item->{'description'})) {
534 $line =~ s/.*src=\"(http:.*\.[jpg][pni][gf])\".*/$1/i and $mnvitem{'thumbnail'} = $line and last;
538 if ($item->{'media'}->{'description'}) {
539 $mnvitem{'description'} = $item->{'media'}->{'description'};
540 } elsif ($item->{'itunes'}->{'summary'}) {
541 $mnvitem{'description'} = $item->{'itunes'}->{'summary'};
542 } elsif ($item->{'description'}) {
543 $mnvitem{'description'} = $item->{'description'};
545 if ($mnvitem{'description'}) {
546 my $scrubber = HTML::Scrubber->new;
547 $scrubber->default(0);
548 $mnvitem{'description'} = $scrubber->scrub($mnvitem{'description'});
549 $mnvitem{'description'} = mnvcleantext($mnvitem{'description'});
550 ($mnvitem{'description'}) or $mnvitem{'description'} = "";
551 $mnvitem{'description'} =~ s/\ / /g;
554 ($item->{'enclosure'}->{'length'}) and $mnvitem{'length'} = mnvcleantext($item->{'enclosure'}->{'length'});
555 if (($mnvitem{'length'}) && $mnvitem{'length'} < 5000) {
556 $mnvitem{'duration'} = $mnvitem{'length'};
557 $mnvitem{'length'} = "";
559 if ($item->{'itunes'}->{'duration'}) {
560 my $tmpdur = mnvcleantext($item->{'itunes'}->{'duration'});
561 my $count = $tmpdur =~ s/(:)/$1/g;
563 my ($mins, $secs) = split(':', $tmpdur);
564 (($mins =~ /^\d+$/) && ($secs =~ /^\d+$/)) and $tmpdur = ($mins * 60) + $secs;
565 } elsif ($count == 2) {
566 my ($hours, $mins, $secs) = split(':', $tmpdur);
567 (($hours =~ /^\d+$/) && ($mins =~ /^\d+$/) && ($secs =~ /^\d+$/)) and $tmpdur = ($hours * 60 * 60) + ($mins * 60) + $secs;
569 (($tmpdur =~ /^\d+$/) && $tmpdur > 0) and $mnvitem{'duration'} = $tmpdur;
572 if ($item->{'media'}->{'restriction'}) {
573 $mnvitem{'country'} = mnvcleantext($item->{'media'}->{'restriction'});
579 # return media type (Not very accurate).
584 $file =~ s/redirect\.mp3//g;
586 if ($type eq "flash") {
587 $file =~ /\.flv/i and return 1;
588 $file =~ /\.m[op4][34v]/i and return 1;
589 } elsif ($type eq "html5") {
590 $file =~ /\.m[op4][4v]/i and return 1;
591 $file =~ /\.ogm/i and return 1;
592 $file =~ /\.ogg/i and return 1;
593 } elsif ($type eq "mp4html5") {
594 $file =~ /\.m[op4][4v]/i and return 1;
595 } elsif ($type eq "ogghtml5") {
596 $file =~ /\.ogm/i and return 1;
597 $file =~ /\.ogg/i and return 1;
598 } elsif ($type eq "video") {
599 $file =~ /\.flv/i and return 1;
600 $file =~ /\.m[op4][4v]/i and return 1;
601 $file =~ /\.mpg/i and return 1;
602 $file =~ /\.mpeg/i and return 1;
603 $file =~ /\.mkv/i and return 1;
604 $file =~ /\.avi/i and return 1;
605 $file =~ /\.wmv/i and return 1;
606 $file =~ /\.as[xf]/i and return 1;
607 $file =~ /\.divx/i and return 1;
608 $file =~ /\.ogm/i and return 1;
609 } elsif ($type eq "audio") {
610 $file =~ /\.m4[ab]/i and return 1;
611 $file =~ /\.mp[23]/i and return 1;
612 $file =~ /\.og[ag]/i and return 1;
613 $file =~ /\.aac/i and return 1;
614 $file =~ /\.ram/i and return 1;
615 $file =~ /\.rm/i and return 1;
616 $file =~ /\.wav/i and return 1;
617 $file =~ /\.flac/i and return 1;
618 $file =~ /\.mka/i and return 1;
619 $file =~ /\.au/i and return 1;