root/tv_grab_au @ 190

Revision 190, 28.4 kB (checked in by lincoln, 7 years ago)

consolidate logging into tv_grab_au via call_prog, remove all logging from shepherd

  • Property svn:executable set to *
Line 
1#!/usr/bin/perl -w
2
3my $version = '0.3.2';
4
5# tv_grab_au
6# "Shepherd"
7# A wrapper for various Aussie TV guide data grabbers
8#
9# Use --help for command-line options.
10#
11# Shepherd is an attempt to reconcile many different tv_grab_au scripts and
12# make one cohesive reliable data set. It works by calling a series of
13# scripts that grab data from a large variety of sources, and then
14# analysing the resulting XML data sets and determining which of the many
15# is the most reliable.
16
17# Shepherd runs in 4 passes:
18#  pass 1: (tv_grab_au)  Checks that all components are up-to-date, auto-
19#                        updates if not.
20#                        Passes control onto shepherd
21#  pass 2: (shepherd)    calls grabbers to fill in missing data
22#  pass 3: (shepherd)    calls reconciler to reconcile overlapping data
23#                        and normalize programme titles to our preferred title
24#  pass 4: (shepherd)    calls postprocessors to postprocess data
25#                        (e.g. flag HDTV programmes, augment with IMDb etc.)
26
27# Changelog:
28# 0.2.31  : split tv_grab_au (install/test/upgrade/enable/disable) from
29#           shepherd (grab/reconcile/postprocess). Previous changelog history
30#           is in shepherd
31# 0.3.0   : This split deserves a real version bump, Linc! :)
32# 0.3.1   : honour $option_ready
33
34BEGIN { *CORE::GLOBAL::die = \&my_die; }
35
36use strict;
37no strict 'refs';
38
39use LWP::UserAgent;
40use Sort::Versions;
41use Cwd;
42use Getopt::Long;
43use Data::Dumper;
44use XMLTV;
45use XMLTV::Ask;
46use POSIX qw(strftime mktime);
47use Date::Manip;
48use Algorithm::Diff;
49use List::Compare;
50use Compress::Zlib;
51
52# ---------------------------------------------------------------------------
53# --- Global Variables
54# ---------------------------------------------------------------------------
55
56my $myprogname = 'tv_grab_au';
57my $progname = 'shepherd';
58
59my $HOME = 'http://www.whuffy.com/shepherd';
60
61my $invoked = Cwd::realpath($0);
62my @options = @ARGV;
63
64# By default, Shepherd runs from ~/.shepherd/. If it's not run as a user,
65# it will try /opt/shepherd/ instead.
66my $CWD = ($ENV{HOME} ? $ENV{HOME} . "/." : "/opt/") . $progname;
67-d $CWD or mkdir $CWD or die "Cannot create directory $CWD: $!";
68chdir($CWD);
69
70my $ARCHIVE_DIR = "$CWD/archive";
71
72my $opt;
73my $pref_title_source;
74my $mirror_site;
75my $debug = 0;
76my $components = { };
77my $region;
78my $channels;
79my $opt_channels;
80my $config_file =   "$CWD/$progname.conf";
81my $channels_file = "$CWD/channels.conf";
82my $log_file = "$CWD/$progname.log";
83my $days = 7;
84
85# OBSOLETE: will be removed
86my $preferred;
87my $title_translation_table;
88my $pref_order;
89
90# ---------------------------------------------------------------------------
91# --- Setup
92# ---------------------------------------------------------------------------
93
94# Any options Shepherd doesn't understand, we'll pass to the grabber(s)
95Getopt::Long::Configure(qw/pass_through/);
96
97&get_initial_command_line_options;
98
99&capabilities if ($opt->{capabilities});
100&description if ($opt->{description});
101&version if ($opt->{version});
102
103$| = 1; 
104print ucfirst($myprogname) . " v$version\n\n";
105
106&help if ($opt->{help});
107
108&read_config_file;
109&read_channels_file;
110
111&get_remaining_command_line_options;
112
113if ($opt->{status})
114{
115    &status;
116    exit;
117}
118
119if ($opt->{show_config})
120{
121    &show_config;
122    exit;
123}
124
125&open_logfile unless ($opt->{nolog});
126
127&process_setup_commands;
128
129# ---------------------------------------------------------------------------
130# --- Update
131# ---------------------------------------------------------------------------
132
133unless ($opt->{noupdate})
134{
135    if (&update()) 
136    {
137        &write_config_file;
138    }
139}
140
141if ($opt->{configure})
142{
143    &configure;
144}
145
146# ---------------------------------------------------------------------------
147# --- Go!
148# ---------------------------------------------------------------------------
149
150unless ($opt->{update})
151{
152    write_config_file();
153    &log("Passing control to Shepherd: $CWD/shepherd @options\n");
154
155    call_prog("$CWD/shepherd @options");
156}
157
158&log("Done.\n");
159&close_logfile() unless $opt->{nolog};
160
161# ---------------------------------------------------------------------------
162# --- Subroutines
163# ---------------------------------------------------------------------------
164
165# -----------------------------------------
166# Subs: Updates & Installations
167# -----------------------------------------
168
169sub update
170{
171    &log("\nChecking for updates:\n\n");
172
173    my $data = fetch_shepherd_file("status");
174
175    return 0 unless ($data);
176
177    my $made_changes = 0;
178    my %clist = %$components;
179
180    while ($data =~ /(\S+)\s+(\S+)\s+(\S+)/g)
181    {
182        my ($progtype, $proggy, $latestversion) = ($1,$2,$3);
183        if (update_component($proggy, $latestversion, $progtype))
184        {
185            $made_changes = 1;
186        }
187        delete $clist{$proggy};
188    }
189
190    # work out what components disappeared (if any)
191    foreach (keys %clist) {
192        unless ($components->{$_}->{disabled}) {
193            &log("\nDeleted component: $_.\n");
194            disable($_, 2);
195            $made_changes = 1;
196        }
197    }
198    $made_changes;
199}
200
201sub update_component
202{
203    my ($proggy, $latestversion, $progtype) = @_;
204
205    my $ver = 0;
206
207    if ($progtype eq "shepherd")
208    {
209        if (-e "$CWD/$proggy")
210        {
211            $ver = `$CWD/$proggy --version`;
212            chop($ver);
213        }
214    } else {
215        $ver = $components->{$proggy}->{ver} if (defined $components->{$proggy} and -e ($progtype . "s/$proggy/$proggy"));
216    }
217
218    my $result = versioncmp($ver, $latestversion);
219    my $action =    $result == -1 ? ($ver ? "UPGRADING" : "NEW") :
220                    $result ==  1 ? "DOWNGRADING" :
221                    "up to date";
222    &log(sprintf    "* %-40s %30s\n",
223                    ucfirst($progtype) . " $proggy" .
224                        ($ver ? " v$ver" : '') . "...",
225                    $action);
226    return 0 unless ($result);
227    install($proggy, $latestversion, $progtype, $ver);
228    return 1;
229}
230
231sub install
232{
233    my ($proggy, $latestversion, $progtype, $oldver) = @_;
234    my $config;
235
236    &log("Downloading $proggy v$latestversion.\n");
237
238    my $rdir = "";
239    my $ldir = query_ldir($proggy, $progtype);
240
241    if ($progtype ne "shepherd") {
242        $rdir = $progtype . "s";
243        -d ("$CWD/$progtype" . "s") 
244            or mkdir ("$CWD/$progtype" . "s") 
245            or die "Cannot create directory $CWD/$progtype" . "s: $!";
246    }
247    -d $ldir or mkdir $ldir or die "Cannot create directory $ldir: $!";
248
249    my $newfile = "$ldir/$proggy-$latestversion";
250    my $rfile = "$rdir/$proggy";
251
252    return unless (fetch_shepherd_file($rfile, $newfile));
253
254    # Fetch grabber config file
255    if ($progtype eq 'grabber')
256    {
257        $rfile .= ".conf";
258        $config = fetch_shepherd_file($rfile);
259        return unless ($config); # grabbers MUST have config files
260        eval $config;
261    }
262
263    # Make component executable
264    chmod 0755,$newfile;
265
266    -d $ARCHIVE_DIR or mkdir $ARCHIVE_DIR or die "Cannot create directory $ARCHIVE_DIR: $!";
267
268    if (-e "$ldir/$proggy")
269    {
270        rename("$ldir/$proggy", "$ARCHIVE_DIR/$proggy-$oldver");
271    }
272    rename($newfile, "$ldir/$proggy");
273   
274    &log("Installed $proggy v$latestversion.\n") if ($debug);
275
276    # if the update was for shepherd itself, restart it
277    if ($progtype eq "shepherd") {
278        &log("\n*** Restarting ***\n\n");
279        &close_logfile unless $opt->{nolog};
280        exec("$CWD/$myprogname @options");
281        # This exits.
282        exit(0);
283    }
284
285    my $result = test_proggy($proggy, $progtype);
286
287    $components->{$proggy}->{type} = $progtype;
288    $components->{$proggy}->{ver} = $latestversion;
289    $components->{$proggy}->{ready} = $result;
290    $components->{$proggy}->{config} = $config if ($progtype eq 'grabber');
291
292    # If this component was disabled automatically, re-enable it.
293    # But if it was disabled manually, leave it off.
294    my $d = $components->{$proggy}->{disabled};
295    if ($d and $d == 2)
296    {
297        enable($proggy);
298    }
299
300    $components->{$proggy}->{laststatus} = sprintf "updated to %s on %s", $latestversion, (strftime "%a%d%b%y",localtime(time));
301
302}
303
304sub fetch_shepherd_file
305{
306    my ($fn, $store) = @_;
307
308    my $sites = "";
309    $sites = "$mirror_site," if ($mirror_site);
310    $sites .= $HOME;
311
312    my $ret;
313    foreach my $site (split(/,/,$sites)) 
314    {
315        $ret = fetch_file("$site/$fn", $store, 1);
316        return $ret if ($ret);
317    }
318    return undef;
319}
320
321sub test_proggy
322{
323    my ($proggy, $progtype) = @_;
324
325    &log("Testing $proggy...\n");
326
327    my $ldir = query_ldir($proggy, $progtype);
328    my $opt_ready = query_config($proggy, 'option_ready');
329    $opt_ready ||= '--version';
330   
331    chdir($ldir);
332    my $result = call_prog(query_filename($proggy, $progtype) . " $opt_ready");
333    chdir ($CWD);
334
335    print "Return value: $result\n" if ($debug);
336
337    if ($result)
338    {
339        &log("\n" . ucfirst($progtype) . " $proggy did not exit cleanly!\n" .
340             "It may require configuration.\n\n");
341    }
342    else
343    {
344        &log("OK.\n");
345    }
346    return !$result;
347}
348
349sub enable
350{
351    my $proggy = shift;
352
353    # confirm it exists first
354    if (!$components->{$proggy}) {
355        printf "No such component: \"%s\".\n",$proggy;
356        return;
357    }
358    print "Enabling $proggy.\n";
359
360    delete $components->{$proggy}->{disabled};
361    $components->{$proggy}->{laststatus} = sprintf "enabled on %s, not run yet",(strftime "%a%d%b%y", localtime(time));
362}
363
364sub disable
365{
366    my ($proggy, $n) = @_;
367
368    # confirm it exists first
369    if (!$components->{$proggy}) {
370        printf "No such component: \"%s\".\n",$proggy;
371        return;
372    }
373    print "Disabling $proggy.\n";
374   
375    $n ||= 1;
376    $components->{$proggy}->{disabled} = $n;
377    $components->{$proggy}->{laststatus} = sprintf "manually disabled on %s",(strftime "%a%d%b%y", localtime(time));
378}
379
380sub check
381{
382    my $result;
383    foreach my $proggy (keys %$components) {
384        my $progtype = $components->{$proggy}->{type};
385        $result = test_proggy($proggy, $components->{$proggy}->{type});
386        if (!$result ne !$components->{$proggy}->{ready}) {
387            $components->{$proggy}->{ready} = $result;
388        }
389    }
390}
391# -----------------------------------------
392# Subs: Utilities
393# -----------------------------------------
394#
395
396sub query_grabbers
397{
398    my ($conf, $val) = @_;
399    return query_component_type('grabber',$conf,$val);
400}
401
402sub query_reconcilers
403{
404    return query_component_type('reconciler');
405}
406
407sub query_postprocessors
408{
409    return query_component_type('postprocessor');
410}
411
412sub query_component_type
413{
414    my ($progtype,$conf,$val) = @_;
415
416    my @ret = ();
417    foreach (keys %$components)
418    {
419        if ($components->{$_}->{type} eq $progtype) {
420            if (defined $conf) {
421                push (@ret, $_) if (query_config($_,$conf) eq $val);
422            } else {
423                push (@ret, $_);
424            }
425        }
426    }
427    return @ret;
428}
429
430sub query_name
431{
432    my $str = shift;
433    if ($str =~ /(.*) \[cache\]/)
434    {
435        return $1;
436    }
437    return $str;
438}
439
440sub query_filename
441{
442    my ($proggy, $progtype) = @_;
443
444    return query_ldir($proggy, $progtype) . "/$proggy";
445}
446
447sub query_ldir
448{
449    my ($proggy, $progtype) = @_;
450    my $ret = $CWD;
451    $ret .= ('/' . $progtype . "s/$proggy") if ($progtype ne 'shepherd');
452    return $ret;
453}
454
455sub query_config
456{
457    my ($grabber, $key) = @_;
458
459    $grabber = query_name($grabber);
460    return undef unless ($components->{$grabber});
461    return $components->{$grabber}->{config}->{$key};
462}
463
464sub rotate_logfiles
465{
466    # keep last 4 log files
467    my $num;
468    for ($num = 4; $num > 0; $num--) {
469        my $f1 = sprintf "%s.%d.gz",$log_file,$num;
470        my $f2 = sprintf "%s.%d.gz",$log_file,$num+1;
471        unlink($f2);
472        rename($f1,$f2);
473    }
474
475    my $f2 = sprintf "%s.1",$log_file;
476    rename($log_file,$f2);
477}
478
479sub compress_file
480{
481    my $infile = shift;
482    my $outfile = sprintf "%s.gz",$infile;
483    my $gz;
484
485    if (!(open(INFILE,"<$infile"))) {
486        warn "could not open file $infile for reading: $!\n";
487        return;
488    }
489
490    if (!($gz = gzopen($outfile,"wb"))) {
491        warn "could not open file $outfile for writing: $!\n";
492        return;
493    }
494
495    while (<INFILE>) {
496        my $byteswritten = $gz->gzwrite($_);
497        warn "error writing to compressed file: error $gz->gzerror"
498          if ($byteswritten == 0);
499    }
500    close(INFILE);
501    $gz->gzclose();
502    unlink($infile);
503}
504
505sub open_logfile
506{
507    &rotate_logfiles;
508    printf "Logging to $log_file.\n";
509    open(LOG_FILE,">>$log_file") || die "can't open log file $log_file for writing: $!\n";
510
511    my $now = localtime(time);
512    printf LOG_FILE "$myprogname version $version started at $now\n\n";
513
514    compress_file($log_file.".1");
515}
516
517sub close_logfile
518{
519    close(LOG_FILE);
520}
521
522sub log
523{
524    my $entry = shift;
525    print $entry;
526    printf LOG_FILE "%s",$entry unless $opt->{nolog};
527}
528
529sub call_prog
530{
531    my $prog = shift;
532    if (!(open(PROG,"$prog|"))) {
533        &log("warning: couldn't exec \"$prog\": $!\n");
534        return -1;
535    }
536    while(<PROG>) {
537        &log($_);
538    }
539    close(PROG);
540
541    if ($? == -1) {
542        &log("Failed to execute prog: $!\n");
543        return -1;
544    } elsif ($? & 127) {
545        &log((sprintf "prog died with signal %d, %s coredump\n",
546          ($? & 127),  ($? & 128) ? "with" : "without"));
547        return $?;
548    } else {
549        &log((sprintf "prog exited with value %d\n", $? >> 8)) if ($debug or $?);
550        return ($? >> 8);
551    }
552}
553
554sub fetch_file
555{
556    my ($url, $store, $id_self) = @_;
557
558    &log("Fetching $url.\n");
559   
560    my $ua = LWP::UserAgent->new();
561    if ($id_self)
562    {
563        $ua->agent(ucfirst("$progname/$myprogname/$version"));
564    }
565    else
566    {
567        $ua->agent('Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322')
568    }
569
570    my $response = $ua->get($url);
571    if ($response->is_success())
572    {
573        if ($store)
574        {
575            open (FILE, ">$store") 
576                or (&log("ERROR: Unable to open $store for writing.\n") and return undef);
577            print FILE $response->content();
578            close FILE;
579            return 1;
580        }
581        else 
582        {
583            return $response->content();
584        } 
585    }
586    &log("Failed to retrieve $url!\n" . $response->status_line() . "\n");
587    return undef;
588}
589
590# -----------------------------------------
591# Subs: Setup
592# -----------------------------------------
593
594sub read_config_file
595{
596    read_file($config_file, 'configuration');
597}
598
599sub read_channels_file
600{
601    read_file($channels_file, 'channels');
602}
603
604sub read_file
605{
606    my $fn = shift;
607    my $name = shift;
608
609    print "Reading $name file: $fn\n";
610    unless (-r $fn)
611    {
612        unless ($opt->{configure})
613        {
614            print "\nNo $name file found.\n" .
615                  ucfirst($progname) . " must be configured: " .
616                  "configuring now.\n\n";
617            $opt->{'configure'} = 1;
618        }
619        return;
620    }
621    local (@ARGV, $/) = ($fn);
622    no warnings 'all';
623    eval <>;
624    if ($@ and !$opt->{configure})
625    {
626        warn "\nERROR in $name file! Details:\n$@";
627        print "You may wish to CTRL-C and fix this.\n\nContinuing anyway in:";
628        foreach (1 .. 5)
629        {
630            print " " . (6 - $_);
631            sleep 1;
632        }
633        print "\n";
634    }
635}
636
637sub write_config_file
638{
639    write_file($config_file, 'configuration', 
640        [$region,  $pref_title_source,  $mirror_site,  $components ],
641        ["region", "pref_title_source", "mirror_site", "components" ]);
642}
643
644sub write_channels_file
645{
646    write_file($channels_file, 'channels',
647        [ $channels,  $opt_channels ],
648        [ 'channels', 'opt_channels' ]);
649}
650
651sub write_file
652{
653    my ($fn, $name, $vars, $varnames) = @_;
654    open (FN, ">$fn") or die "Can't write to $name file $fn: $!";
655    print FN Data::Dumper->Dump($vars, $varnames);
656    close FN;
657    print "Wrote $name file $fn.\n" if ($debug);
658}
659
660sub get_initial_command_line_options
661{
662  GetOptions( 'config-file=s'   => \$opt->{configfile},
663              'help'            => \$opt->{help},
664              'configure'       => \$opt->{configure},
665              'setmirror=s'     => \$opt->{setmirror},
666              'setpreftitle=s'  => \$opt->{setpreftitlesource},
667              'clearpreftitle'  => \$opt->{clearpreftitlesource},
668              'dontcallgrabbers' => \$opt->{dontcallgrabbers},
669             
670              # http://xmltv.org/wiki/xmltvcapabilities.html
671              'capabilities'    => \$opt->{capabilities},
672              'description'     => \$opt->{description},
673              'quiet'           => \$opt->{quiet},
674              'version'         => \$opt->{version},
675
676              'debug'           => \$debug);
677}
678
679sub get_remaining_command_line_options
680{
681    GetOptions(
682              'status'          => \$opt->{status},
683              'list'            => \$opt->{list},
684              'show-config'     => \$opt->{show_config},
685
686              'update'          => \$opt->{update},
687              'noupdate'        => \$opt->{noupdate},
688
689              'disable=s'       => \$opt->{disable},
690              'enable=s'        => \$opt->{enable},
691
692              'nolog'           => \$opt->{nolog},
693
694              'days=i'          => \$days,
695              'offset=i'        => \$opt->{offset},
696              'show-channels'   => \$opt->{show_channels},
697              'output=s'        => \$opt->{output},
698              'randomize'       => \$opt->{randomize}, # experimental
699              'check'           => \$opt->{check}
700            );
701}
702
703sub process_setup_commands
704{
705    my @opts = qw( enable disable setorder check \
706                   setpreftitlesource clearpreftitlesource setmirror );
707
708    my $run = 0;
709    foreach (@opts)
710    {
711        if ($opt->{$_})
712        {
713            $run = 1;
714            &$_($opt->{$_});
715        }
716    }
717    return unless ($run);
718    write_config_file();
719    status();
720    exit;
721}
722
723# if a preferred title source has been specified, add it to our config
724sub setpreftitlesource
725{
726    my $arg = shift;
727    $pref_title_source = $arg;
728    print "Added preferred title source: $pref_title_source\n";
729    1;
730}
731
732# if requesting to clear preferred title and we have one, remove it
733sub clearpreftitlesource
734{
735    $pref_title_source = undef;
736    print "Removed preferred title source $pref_title_source\n";
737    1;
738}
739
740# if a mirror has been specified, add it into our config
741sub setmirror
742{
743    my $arg = shift;
744    $mirror_site = $arg;
745    print "Setting mirror site(s): $mirror_site\n";
746}
747
748# -----------------------------------------
749# Subs: Configuration
750# -----------------------------------------
751
752sub configure
753{
754    my $REGIONS = {
755        "ACT" => 126,
756        "NSW: Sydney" => 73,
757        "NSW: Newcastle" => 184,
758        "NSW: Central Coast" => 66,
759        "NSW: Griffith" => 67,
760        "NSW: Broken Hill" => 63,
761        "NSW: Northern NSW" => 69,
762        "NSW: Southern NSW" => 71,
763        "NSW: Remote and Central" => 106,
764        "NT: Darwin" => 74,
765        "NT: Remote & Central" => 108,
766        "QLD: Brisbane" => 75,
767        "QLD: Gold Coast" => 78,
768        "QLD: Regional" => 79,
769        "QLD: Remote & Central" => 114,
770        "SA: Adelaide" => 81,
771        "SA: Renmark" => 82,
772        "SA: Riverland" => 83,
773        "SA: South East SA" => 85,
774        "SA: Spencer Gulf" => 86,
775        "SA: Remote & Central" => 107,
776        "Tasmania" => 88,
777        "VIC: Melbourne" => 94,
778        "VIC: Geelong" => 93,
779        "VIC: Eastern Victoria" => 90,
780        "VIC: Mildura/Sunraysia" => 95,
781        "VIC: Western Victoria" => 98,
782        "WA: Perth" => 101,
783        "WA: Regional" => 102
784    };
785
786    print "\nConfiguring.\n\n" .
787          "Select your region:\n";
788    foreach (sort keys %$REGIONS)
789    {
790        printf(" (%3d) %s\n", $REGIONS->{$_}, $_);
791    }
792    $region = ask_choice("Enter region code:", "94", values %$REGIONS);
793
794    print "\nFetching channel information... ";
795
796    my @channellist = get_channels();
797
798    print "done.\n\n" .
799          "For each channel you want guide data for, enter an XMLTV id\n" .
800          "of your choice (e.g. \"seven.free.au\"). If you don't need\n" .
801          "guide data for this channel, just press Enter.\n\n" .
802          "Please don't subscribe to unneeded channels.\n\nChannels:\n";
803    $channels = {};
804    my $line;
805    foreach (@channellist)
806    {
807        $line = ask(" \"$_\"? ");
808        $channels->{$_} = $line if ($line);
809    }
810
811    print "\nHigh Definition TV (HDTV)\n".
812          "Most Australian TV networks broadcast at least some\n".
813          "programmes in HDTV each week, but for the most part\n".
814          "either upsample SD to HD or play a rolling demonstration\n".
815          "HD clip when they don't have the programme in HD format.\n\n".
816          "If you have a HDTV capable system and are interested in\n".
817          "having Shepherd's postprocessors populate HDTV content\n".
818          "then Shepherd will need to know the XMLTV IDs for the HD\n".
819          "channels also.\n";
820    if (ask_boolean("\nDo you wish to include HDTV channels?")) {
821        print "\nFor each channel you want guide data for, enter an XMLTV id\n" .
822              "of your choice (e.g. \"sevenhd.free.au\"). If you don't need\n" .
823              "guide data for this channel, just press Enter.\n\n";
824
825        $opt_channels = {};
826        foreach (@channellist)
827        {
828            next if (($_ =~ /ABC2/i) || ($_ =~ /SBS News/i) || ($_ =~ /31/));
829            $_ .= "HD";
830            $line = ask(" \"$_\"? ");
831            $opt_channels->{$_} = $line if ($line);
832        }
833    }
834
835
836    print "\nWould you like to transition seamlessly from your current grabber?\n\n".
837          "Different data sources can have different names for the same show. For\n".
838          "example, one grabber might call a show \"Spicks & Specks\" while another\n".
839          "calls it \"Spicks and Specks\". These differences can make MythTV think\n".
840          "they're actually different shows.\n\n".
841          ucfirst($progname) . " is able to merge these differences so that it always\n".
842          "presents shows with a consistent name, no matter where it actually sourced\n".
843          "show data from. If you'd like, it can also rename shows so they're consistent\n".
844          "with whichever grabber you've been using until now.\n\n".
845          "The advantage of this is that you should get a smoother transition to\n".
846          ucfirst($progname) . ", with no shows changing names and no need to re-create\n".
847          "any recording rules. The main disadvantage is that if your previous grabber\n".
848          "used an inferior data source -- i.e. it sometimes has typos or less\n".
849          "informative program names -- then you'll continue to see these.\n\n".
850          "If you were using one of the following grabbers previously AND you want\n".
851          ucfirst($progname) . " to use that grabber's program names, select it here.\n\n";
852
853    my $def = "Do not transition; just use best quality titles";
854    my %transition = (  "ltd (aka tv_grab_au, versions 1,30, 1.40 or 1.41)" => "yahoo7widget,abc2_website",
855                        "OzTivo" => 'oztivo',
856                        "Rex" => 'rex',
857                        "JRobbo" => 'jrobbo' );
858    my $pref = ask_choice("Transition from grabber?", $def,
859                $def, keys %transition);
860    $pref_title_source = $transition{$pref};
861   
862    print "\n";
863    show_channels();
864    unless(ask_boolean("\nCreate configuration file?"))
865    {
866        print "Aborting configuration.\n";
867        exit 0;
868    }
869
870    write_config_file();
871    write_channels_file();
872
873    print "Finished configuring.\n\n" .
874          "Shepherd is installed into $CWD.\n\n";
875   
876    if ($invoked ne "$CWD/$myprogname" and $invoked =~ /$myprogname/)
877    {
878        print "Warning: you invoked this program as $invoked.\n" .
879            "In the future, it should be run as $CWD/$myprogname,\n" .
880            "to avoid constantly re-downloading the latest version.\n\n" .
881            "MythTV users may wish to create the following symlink, by " .
882            "doing this (as root):\n" .
883            "\"ln -s $CWD/$myprogname /usr/bin/tv_grab_au\".\n\n" .
884            "You may safely delete $invoked.\n\n";
885    }
886
887    status();
888
889    unless (ask_boolean("\nGrab data now?"))
890    {
891        exit 0;
892    }
893}
894
895sub get_channels
896{
897    my @date = localtime;
898    my $page = fetch_file(
899        "http://au.tv.yahoo.com/results.html?rg=$region&dt=" .
900        ($date[5] + 1900) . "-$date[4]-$date[3]");
901    my @channellist;
902    while ($page =~ /<tr class=rtb><td class=rth><a .*?>(.*?)<\/a>/g)
903    {
904        push @channellist, $1;
905    }
906    return @channellist;
907}
908
909# -----------------------------------------
910# Subs: Status & Help
911# -----------------------------------------
912
913sub show_config
914{
915    print "\nConfiguration\n".
916          "-------------\n" .
917          "Config file: $config_file\n" .
918          "Debug mode : " . is_set($debug) . "\n" .
919          "Output file: " . ($opt->{output} ? $opt->{output} : "None") . "\n" .
920          "Region ID  : $region\n";
921  show_channels();
922  print "\n";
923  status();
924  print "\n";
925}
926
927sub show_channels
928{
929  print "Subscribed channels:\n";
930  print "    $_ -> $channels->{$_}\n" for sort keys %$channels;
931  print "Optional (HDTV) channels:\n";
932  print "    $_ -> $opt_channels->{$_}\n" for sort keys %$opt_channels;
933}
934
935sub is_set
936{
937    my $arg = shift;
938    return $arg ? "Yes" : "No";
939}
940
941sub pretty_print
942{
943    my ($p, $len) = @_;
944    my $spaces = ' ' x (79-$len);
945    my $ret = "";
946
947    while (length($p) > 0) {
948        if (length($p) <= $len) {
949            $ret .= $p;
950            $p = "";
951        } else {
952            # find a space to the left of cutoff
953            my $len2 = $len;
954            while ((substr($p,$len2,1) ne ' ') && ($len2 > 0)) {
955                $len2--;
956            }
957            if ($len2 == 0) {
958                # no space - just print it with cutoff
959                $ret .= substr($p,0,$len);
960                $p = substr($p,$len,(length($p)-$len));
961            } else {
962                # print up to space
963                $ret .= substr($p,0,$len2);
964                $p = substr($p,($len2+1),(length($p)-$len2+1));
965            }
966            # print whitespace
967            $ret .= "\n".$spaces;
968        }
969    }
970    return $ret;
971}
972
973sub status
974{
975    print "\nThe following plugins are known:\n",
976          " Type     Name           Version Description\n".
977          " -------- -------------- ------- ----------------------------------------------\n";
978
979    foreach (sort { $components->{$a}->{type} cmp $components->{$b}->{type} } keys %{$components}) {
980        printf " %-8s %-15s%7s %46s\n",
981          substr($components->{$_}->{type},0,8), 
982          length($_) > 15 ? substr($_,0,13).".." : $_,
983          ($components->{$_}->{ver} ? substr($components->{$_}->{ver},0,7) : "unknown"),
984          (defined $components->{$_}->{config}->{desc} ?
985            pretty_print($components->{$_}->{config}->{desc},46) : "");
986    }
987    printf "\n";
988
989    print "Grabbers, listed in order of quality:\n".
990          "                   Enabled/\n".
991          " Grabber        Qual Ready Last Run   Status\n" .
992          " -------------- ---- ----- ---------- -----------------------------------------\n";
993    my %qual_table = ( 3 => "Best", 2 => "Good", 1 => "Avg" );
994    foreach (sort { $components->{$b}->{config}->{quality} <=> $components->{$a}->{config}->{quality} } query_grabbers()) {
995        my $h = $components->{$_};
996        printf  " %-15s%-4s  %1s/%1s %11s %s\n",
997          length($_) > 15 ? substr($_,0,13).".." : $_,
998          $qual_table{($h->{config}->{quality})},
999          $h->{disabled} ? 'N' : 'Y',
1000          $h->{ready} ? 'Y' : 'N',
1001          $h->{lastdata} ? (strftime "%a%d%b%y", localtime($h->{lastdata})) : 'never',
1002          $h->{laststatus} ? pretty_print($h->{laststatus},41) : '';
1003    }
1004
1005    print "\n".
1006          "              Enabled/\n".
1007          " Reconciler     Ready Last Run   Status\n" .
1008          " -------------- ----- ---------- ----------------------------------------------\n";
1009    foreach (sort { $components->{$a} <=> $components->{$b} } query_reconcilers()) {
1010        my $h = $components->{$_};
1011        printf  " %-15s %1s/%1s %11s %s\n",
1012          length($_) > 15 ? substr($_,0,13).".." : $_,
1013          $h->{disabled} ? 'N' : 'Y',
1014          $h->{ready} ? 'Y' : 'N',
1015          $h->{lastdata} ? (strftime "%a%d%b%y", localtime($h->{lastdata})) : 'never',
1016          $h->{laststatus} ? pretty_print($h->{laststatus},46) : '';
1017    }
1018
1019    print "\n".
1020          "              Enabled/\n".
1021          " Postprocessor  Ready Last Run   Status\n" .
1022          " -------------- ----- ---------- ----------------------------------------------\n";
1023    foreach (sort { $components->{$a} <=> $components->{$b} } query_postprocessors()) {
1024        my $h = $components->{$_};
1025        printf  " %-15s %1s/%1s %11s %s\n",
1026          length($_) > 15 ? substr($_,0,13).".." : $_,
1027          $h->{disabled} ? 'N' : 'Y',
1028          $h->{ready} ? 'Y' : 'N',
1029          $h->{lastdata} ? (strftime "%a%d%b%y", localtime($h->{lastdata})) : 'never',
1030          $h->{laststatus} ? pretty_print($h->{laststatus},46) : '';
1031    }
1032    printf "\nPreferred titles from grabber '%s'\n",$pref_title_source if ($pref_title_source);
1033    printf "\n";
1034}
1035
1036sub capabilities
1037{
1038    print "baseline\nmanualconfig\n";
1039    exit 0;
1040}
1041
1042sub description
1043{
1044    print "Australia\n";
1045    exit 0;
1046}
1047
1048sub version
1049{
1050    print "$version\n";
1051    exit 0;
1052}
1053
1054sub help
1055{
1056    print q{
1057Command-line options:
1058    --help                Print this message
1059
1060    --status              Print a list of grabbers maintained
1061    --list                Print a detailed list of grabbers
1062    --setmirror <s>       Set URL <s> as primary location to check for updates
1063
1064    --configure           Setup
1065    --show-config         Print setup details
1066
1067    --setpreftitle <s>    Set preferred 'title' source as grabber <s>
1068    --clearpreftitle      clear preferred 'title' source
1069
1070    --disable <s>         Don't ever use grabber/postprocessor <s>
1071    --enable <s>          Okay, maybe use it again then
1072    --uninstall <s>       Remove a disabled grabber/postprocessor
1073
1074    --noupdate            Do not attempt to update before running
1075    --update              Update only; do not grab data
1076
1077    --check               Check status of all grabbers and postprocessors
1078
1079    --capabilities        Report capabilities to XMLTV
1080
1081    --nolog               Don't write a logfile
1082};
1083    exit 0;
1084}
1085
1086# -----------------------------------------
1087# Subs: override handlers for standard perl.
1088# -----------------------------------------
1089
1090# ugly hack. please don't try this at home kids!
1091sub my_die {
1092    my ($arg,@rest) = @_;
1093    my ($pack,$file,$line,$sub) = caller(0);
1094
1095    # check if we are in an eval()
1096    if ($^S) {
1097        printf STDERR "  shepherd caught a die() within eval{} from file $file line $line\n";
1098    } else {
1099            printf STDERR "\nDIE: line %d in file %s\n",$line,$file;
1100            if ($arg) {
1101                CORE::die($arg,@rest);
1102            } else {
1103                CORE::die(join("",@rest));
1104            }
1105    }
1106}
Note: See TracBrowser for help on using the browser.