root/tv_grab_au @ 187

Revision 187, 28.5 kB (checked in by max, 7 years ago)

Respect option_ready; version bump for split tv_grab_au

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