root/tv_grab_au @ 182

Revision 182, 29.3 kB (checked in by lincoln, 7 years ago)

first pass at splitting update/install/check/enable/disable from main shepherd script

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