Stories
Slash Boxes
Comments
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

Friday May 21, 2004
03:29 AM

Radio programs, calendars, Date::Simple and avoiding the web

[ #18865 ]

I like to listen to particular NPR programs while I work, but I do not like stopping whatever I am doing to get the next show going. NPR changed the way they structured the web, making it much more baroque and broken and stupid. They previously had a "current show" link, which always led to the audio of the last show (no matter the date it might be when I select it).

Now NPR has this really goofed, two-layer JavaScript thing going on. The previously non-stupid and convenient link is now a JavaScript action which has to look in a couple different places to figure out what to do.

It is apparent why they did this, although not why they should. They now offer audio in Windows Media Format, so they have extra options in the process. They also standardize the audio links among all of their programs. Behind the scenes, I think they also do some round-robin load balancing.

That does not help me much though. I want to click once to start listening to a show. I do not want to select a bookmark, find the link, then click on it. Since they went to this goofy JavaScript action link model, I have to do a lot of work.

I wrote a little CGI script to get around this. If I know the show's code, the date, and the media type (RealAudio), I can construct the right link. This script only create links valid on for that day. If I call the script at 1 am (not an unusual time for me to work), the script tries to redirect me to a URL that spits out null files (since those shows have not yet aired). In short, this script works within its very narrowly defined parameters, which you should read as "is stupid".

#!/usr/bin/perl
 
=head1 NAME
 
npr_today.cgi - redirect to current National Public Radio shows
 
=head1 SYNOPSIS
 
        http://www.example.com/cgi-bin/npr-today.cgi?ATC
        http://www.example.com/cgi-bin/npr-today.cgi?ME
 
=head1 DESCRIPTION
 
This script computes the current URL for a National Public Radio
audio feed (which depends on the date), then redirects to that URL.
This process skips a lot of javascript which www.npr.org uses to
figure this out.
 
=head2 Show codes
 
=over 4
 
=item ATC
 
All Things Considered
 
=item ME
 
Morning Edition
 
=back
 
=head1 SOURCE AVAILABILITY
 
This source is part of a SourceForge project which always has the
latest sources in CVS, as well as all of the previous releases.
 
        http://sourceforge.net/projects/brian-d-foy/
 
If, for some reason, I disappear from the world, one of the other
members of the project can shepherd this module appropriately.
 
=head1 AUTHOR
 
brian d foy, E<lt>bdfoy@cpan.orgE<gt>
 
=head1 COPYRIGHT
 
Copyright 2004, brian d foy, All rights reserved.
 
You may use this program under the same terms as Perl itself.
 
=cut
 
my %show_codes = map { $_, 1 } qw( ATC ME FA );
 
my $show_code = $ENV{QUERY_STRING};
 
unless( exists $show_codes{ $show_code } )
        {
        print <<"HTTP";
Status: 408 Bad Request
Content-type: text/plain
 
I do not recognize the NPR show code [$Show_code].
HTTP
 
        exit;
        }
 
my @months = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec );
my( $day, $month, $year ) = (localtime)[3,4,5];
 
my $date = sprintf "%02d-%s-%4d",  $day, $months[$month], $year + 1900;
 
my $url = join '',
                "http://www.npr.org/dmg/dmg.php?prgCode=",
                $show_code,
                "&showDate=",
                $date,
                "&segNum=&mediaPref=RM";
 
print <<"HTTP";
Status: 303 See other
Location: $url
 
<html><head><title>Redirect to $url</title></head><body>
Redirect to <a href="$url">$url</a>
</body></html>
HTTP

This script also ignores another problem: What if I missed yesterday's shows? I can only get the current ones. This script also does not handle the weekend shows.

I did not look forward to the idea of making this script understand yesterday, the week before, or anything like that. With a lot of extra programming I could do that, and it certainly is not hard: it is just boring programming.

Instead, I decided to create a calendar for all of the links for this month, and, while I am at it, all of the links for last month, in case I am in the first couple days of the month. I ended up using HTML::Calendar::Simple and Date::Simple. Why didn't I know about Date::Simple before? It rocks. And, check out that first map: I iterate through method names, not objects. :)

#!/usr/bin/perl
use strict;
use warnings;
 
use Date::Simple qw(:all);
use HTML::Calendar::Simple;
 
my %shows = (
        weekend => [ qw(WESAT WESUN) ],
        weekday => [ qw(ME ATC FA)   ],
        );
 
my $this = [ map { today()->$_ } qw(year month) ];
my $last =
        $this->[1] == 1                        ?
                [ $this->[0] - 1, 12             ] :
                [ $this->[0],     $this->[1] - 1 ];
 
my @months = map { HTML::Calendar::Simple->new(
                        { year => $_->[0], month => $_->[1] }
                        )
                } ( $this, $last );
 
foreach my $this_month ( @months )
        {
        my( $year, $month ) = map { $this_month->$_ } qw(year month);
 
        foreach my $this_day ( 1 .. days_in_month( $year, $month ) )
                {
                my $date     = ymd( $year, $month, $this_day );
                my $npr_date = $date->format( "%d-%B-%Y" );
                my $weekday  = $date->day_of_week() % 6;
 
                my $shows   = $weekday ? $shows{weekday} : $shows{weekend};
 
                foreach my $show ( @$shows )
                        {
                        my $link = ram_href( $show, $npr_date );
                        my $href = qq|<a href="$link">$show</a>|;
                        $this_month->daily_info( {
                                day    => $this_day,
                                $show  => $href,
                                });
                        }
                }
        }
 
my $html = join '', map { qq|<td class="wrapper">| . $_->calendar_month . "</td>\n" }
        @months;
 
         print <<"HERE";
<html>
<head><title>brian's NPR Listening Calendar</title>
<link rel="stylesheet" type="text/css" href="npr_calendar.css" />
</head>
<body>
<h1>brian's NPR Listening Calendar</h1>
 
<table><tr>
$html
</tr></table>
 
</body>
</html>
HERE
 
    sub ram_href
        {
        "http://www.npr.org/dmg/dmg.php?prgCode=" .
        $_[0]                                     .
        "&showDate="                              .
        $_[1]                                     .
        "&segNum=&mediaPref=RM";
        }

I run the script through cron at the start of the month and save the output as a static file.

This is quite kludgey. The output is ugly, although I cleaned it up slightly with a style sheet. However, it solves my problem, and has a cool hidden feature: the shows I have already listened to show up as visited links (and if I really wanted to do work, I could make the visitied link color the same as the background). The browser knows what I have visited even if I got there through another path, such as the NPR web site or my earlier script.