xmltv

 view release on metacpan or  search on metacpan

choose/tv_pick/tv_pick_cgi  view on Meta::CPAN

use strict;
use XMLTV qw<best_name write_data>;
use Fcntl ':flock';
use Date::Manip;
use File::Copy;

# Use Log::TraceMessages if installed.
BEGIN {
    eval { require Log::TraceMessages };
    if ($@) {
	*t = sub {};
	*d = sub { '' };
    }
    else {
	*t = \&Log::TraceMessages::t;
	*d = \&Log::TraceMessages::d;
	Log::TraceMessages::check_argv();
	$Log::TraceMessages::CGI = 1;
    }
}

# Use Lingua::EN::Numbers::Ordinate if possible, else homebrew.
sub my_ordinate {
    for ($_[0]) {
	/1$/ && return $_ . 'st';
	/2$/ && return $_ . 'nd';
	/3$/ && return $_ . 'rd';
	return $_ . 'th';
    }
}
BEGIN {
    eval { require Lingua::EN::Numbers::Ordinate };
    if ($@) { *ordinate = \&my_ordinate }
    else { *ordinate = \&Lingua::EN::Numbers::Ordinate::ordinate }
}

# Load CGI last of all so that harmless failures in loading
# not-really-needed modules don't produce errors.
#
use CGI qw<:standard -newstyle_urls>;
use CGI::Carp qw<fatalsToBrowser carpout>; BEGIN { carpout(\*STDOUT) }

########
# Configuration

# Maximum number of programmes to display in a single page.
my $CHUNK_SIZE = 100;

# Input file containing all TV listings.
my $LISTINGS = 'tv.xml';

# Scratch file for storage between requests (this should really be
# done with form data or cookies).
#
my $TOWATCH = 'towatch.tmp';

# Final output file
my $OUTPUT = 'towatch.xml';

# Input file containing preferences (killfiled programmes, etc).
my $PREFS_FILE = 'tvprefs';

# Preferred languages - if information is available in several
# languages, the ones in this list are used if possible.  List in
# order of preference.  Passed to best_name().
#
# FIXME should find this out from HTTP headers.
#
my @PREF_LANGS;

# Hopefully the environment variable $LANG will be set
my $el = $ENV{LANG};
if (defined $el and $el =~ /\S/) {
    $el =~ s/\..+$//; # remove character set
    @PREF_LANGS = ($el);
}
else {
    @PREF_LANGS = ('en'); # change for your language - or just set $LANG
}

########
# End of configuration

# Prototype declarations
sub store_prefs($$);
sub display_form($);
sub print_date_for($;$);
sub clumpidx_to_english($);
sub download_xml();

# Load data into globals $data and @programmes.
my $data = XMLTV::parsefile($LISTINGS);
my $encoding = $data->[0];
my @programmes = @{$data->[3]};

if (url_param('download')) {
    download_xml();
    exit();
}

# Newer versions of CGI.pm have support for <meta http-equiv> stuff.
# But for the moment, we'll keep compatibility with older ones.
#
# We assume the encoding used for listings data is a superset of
# ASCII.
#
print header({ expires => 'now',
	       'Content-Type' => "text/html; charset=$encoding" });

print <<END
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
	      "http://www.w3.org/TR/html4/loose.dtd">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=$encoding">
    <title>TV listings</title>
    <!-- I should be grateful if someone could tell me how to get
      left-aligned text and right-aligned text *on the same line*.
      I am trying to do that with 'category' but I cannot make it
      work. -->
    <style type="text/css"> <!--
        .job { font-weight: bolder }
        .category { float: right; font-style: italic }
        .clumpidx { font-weight: bolder }
    --> </style>
  </head>
END
;

# %wanted
#
# Does the user wish to watch a programme?
#
# Maps title to:
#   undef     -  this programme is not known
#   'never'   -  no, the user never watches this programme
#   'no'      -  probably not, but ask
#   'yes'     -  probably, but ask
#   'always'  -  yes, the user always watches this programme
#
# Read in from the file $PREFS_FILE.
#
my %wanted = ();

# Open for 'appending' - but really we just want to create an empty
# file if needed.
#
open(PREFS, "+>>$PREFS_FILE") or die "cannot open $PREFS_FILE: $!";
flock(PREFS, LOCK_SH);
seek PREFS, 0, 0;
while (<PREFS>) {
    s/\#.*//; s/^\s+//; s/\s+$//;
    next if $_ eq '';
#    t("got line from $PREFS_FILE: " . d($_));
    if (/^(never|no|yes|always): (.+)$/) {
	my ($pref, $prog) = ($1, $2);
	$wanted{$prog} = $pref;
    }
    else { die "$PREFS_FILE:$.: bad line (remnant is $_)\n" }
}
#t('\%wanted=' . d(\%wanted));

my ($skip, $next) = (url_param('skip'), url_param('next'));
foreach ($skip, $next) {
    die "bad URL parameter $_" if defined and tr/0-9//c;
}
#t('$skip=' . d($skip) . ', $next=',  d($next));

if (defined $skip and defined $next) {
    # Must be that the user has submitted some preferences.
    store_prefs($skip, $next);
}
elsif (defined $skip and not defined $next) {
    # This is one of the form pages, skipping some programmes already
    # seen.
    #
    close PREFS;
    display_form($skip);
}
elsif (not defined $skip and not defined $next) {
    # Initial page, corresponding to skip=0.
    if (-e $TOWATCH) {
	if (-M _ < -M $LISTINGS) {
	    print p <<END
The temporary file $TOWATCH already exists and is newer than $LISTINGS - 
refusing to overwrite it.
END
  ;
	    print end_html();
	    exit();
	}
	else {
	    unlink $TOWATCH or die "cannot unlink $TOWATCH: $!";
	}
    }

    # Should really have file locking here.
    open(TOWATCH, ">>$TOWATCH") 
      or die "cannot append to $TOWATCH: $!";
    print TOWATCH <<END
# 'towatch.tmp' file
#
# This file was created by $0 and contains the numbers of programmes
# that the user has chosen to watch, either by giving a preference of
# 'yes' or 'always', or because the stored preference for that
# programme was 'always'.
#
# The format is 'filename/number' on each line.
#
# When the user has finished picking programmes, $0 should reread this
# file and make an XML file to download.
#

END
  ;
    close TOWATCH;

    close PREFS;
    display_form(0);
}
else { die 'bad URL parameters' }


# store_prefs()
#
# Store the user's preferences for $CHUNK_SIZE programmes starting
# from 'skip'.
#
# Parameters:
#   number of programmes to skip from the beginning of @programmes
#   the new value of 'skip' for the next page in the list
#
sub store_prefs($$) {
    die 'usage: store_prefs(skip, next)' if @_ != 2;
    my ($skip, $next) = @_;

    for (my $i = 0; $i < @programmes; $i++) {
	my $val = param("prog$i");
	if (defined $val) {
	    # Check that this programme really did appear in the
	    # previous page.
	    #
	    die "bad programme number $i for skip $skip, next $next"
	      unless $skip <= $i and $i < $next;

	    my $title = best_name(\@PREF_LANGS, $programmes[$i]->{title})->[0];
	    print "$title: $val<br>\n";

	    my $found = 0;
	    foreach (qw[never no yes always]) {
		if ($val eq $_) {
		    $wanted{$title} = $val;
		    $found = 1;
		    last;
		}
	    }
	    die "bad preference '$val' for prog$i" unless $found;
	}
    }

    # Update $PREFS_FILE with preferences.  'yes' or 'no' preferences
    # are still worth storing because they let us pick the default
    # radio button next time.
    #
    copy($PREFS_FILE, "$PREFS_FILE.old")
      or die "cannot copy $PREFS_FILE to $PREFS_FILE.old: $!";
    flock(PREFS, LOCK_EX);
    truncate PREFS, 0 or die "cannot truncate $PREFS_FILE: $!";
    print PREFS <<END
# 'prefs' file
#
# This file contains stored preferences for programme titles, so that
# the user need never be bothered about these shows again.  It's like
# a killfile.  But as well as saying you 'never' want to watch 'That's
# Esther', you can have a preference of 'always' watching some
# programmes, without being asked.
#
# A 'yes' or 'no' preference will change the default choice, but the
# user will be asked again to check.
#
# Generated by $0.
#

END
  ;
    foreach (sort keys %wanted) {
	my $pref = $wanted{$_};
	print PREFS "$pref: $_\n";
    }

    print p(strong("Preferences saved in $PREFS_FILE"));

    # Write out the list of programmes that the user wants to watch
    # this week.  For the time being, we do this as a list of numbers
    # that must be processed later.
    #
    open(TOWATCH, ">>$TOWATCH") or die "cannot append to $TOWATCH: $!";
    flock(TOWATCH, LOCK_EX);
    for (my $i = $skip; $i < $next; $i++) {
	my $val = param("prog$i");
	my $title = best_name(\@PREF_LANGS, $programmes[$i]->{title})->[0];
	if ((defined $wanted{$title} and $wanted{$title} eq 'always')
            or (defined $val and $val eq 'yes') )
        {
	    print TOWATCH "$LISTINGS/$i\n";
	    print br(), "Planning to watch $title\n";
	}
    }
    close TOWATCH;
    print p(strong("List of programme numbers to watch added to $TOWATCH"));

    my $url = url(-relative => 1);
    if ($next >= @programmes) {
	write_output();
	print p <<END
Finished choosing listings.  You can now download
<a href="$OUTPUT">an XML file of the programmes to watch</a>.
END
  ;
    }
    else {
	print a({ href => "$url?skip=$next" }, "Next page");
    }
    print end_html();
    exit();
}


# display_form()
#



( run in 2.218 seconds using v1.01-cache-2.11-cpan-5c0b1e786e0 )