spiff with a silent X

I’ve been playing with XSPF, mostly so I can use the XSPF Web Music Player. There’s a Perl API for working with XSPF (XML::XSPF) which works well, but is extremely short on documentation.

Creating a playlist with XML::XSPF is pretty logical: create a new track object for each new track, then feed an array of these tracks into the playlist object. It took me a couple of hours of fiddling about (and much use of Data::Dumper::Simple, the plain man’s guide to tortuous data structures) to find that out.

The end result is this:
id32xspf – create XSPF playlist to stdout from a list of MP3s with ID3v2 tags.
It’s intended for use on a local directory of MP3s, which will subsequently be uploaded to a website. It uses MP3::Info to do the tag work.
It has some limitations:

  • every file must have ID3v2 tags.
  • it doesn’t handle file:// locations at all well, as their syntax is system-dependent. You’ll probably have to use the --urlbase option. For example, for Unix systems for local files in the current directory, I find -u file://`pwd`/ works well.
  • it doesn’t include track numbers, as I didn’t know that XSPF supported them.
  • it doesn’t create track artwork links, as this isn’t included in ID3 data.

One slightly amusing caveat about the XSPF Web Music Player is that it doesn’t understand the rate of some of lame‘s more amusing VBR presets. If you feed it files from the voice preset (56kbit, mono, resampled to 32000Hz), the results sound like Pinky & Perky

the commitments

When I was testing BlackBerry typed-alike words (dactonyms?) I found that sqlite was averaging about 1 insert per second. This is by no means good.

It turns out that, under Perl, sqlite auto-commits after every write. This slows things down terribly. Here’s how to fix this:

When opening the database handle, turn AutoCommit off:

my $dbh =
DBI->connect( “dbi:SQLite:bberry2.sqlite”, “”, “”, { AutoCommit => 0 } )
or die “$!”;

Then, only commit occasionally — say every thousand writes:

while ( … ) {

…$id++;
$dbh->commit unless ( $id % 1000 );

}
$dbh->commit;

It works out about 1000 times quicker this way.

the disgruntled cyclops in your computer

this perl operator is really a disgruntled cyclops

You might see this in Perl if, for instance, $data were a reference to an array of arrays, and so @{ $data[$#data] } would represent the last row of data in the array. You don’t see it that often; probably more frequently than a real disgruntled cyclops, though …

we’re shite and we … invented the modern world

(a rant for St Andrew’s Day)

It must have been great to be part of the Scottish Enlightenment. This wee country seemed to blossom, from a muddy backwater to a world leader in economics, philosophy, mathematics and engineering.

And yet, for the average Scot, all that was a long time ago. All it seems we can manage now is to churn out neds by the million. So how did we get from the place described (rather breathlessly) in Arthur Herman’s How The Scots Invented The Modern World to the place where the football fans chant “We’re Shite, And We Know We Are.“?

Urban disenfranchisement of the formerly agrarian workforce, perhaps? Who can say. We even chose the darkest, grimmest part of the year for our national day (hint: St Jean-Baptiste would make a smashing national day …). So, have a happy St Andy’s, get properly munted, and wha’s like us, eh?

dealbreakers

Okay, so if I were to buy an iBook, I must be able to:

  • have virtual workspaces, like X11
  • use a compose key for accented characters
  • be able to do my usual Perl/Bash things in the terminal
  • get basic, useful applications for free.

Since I can do these things on Linux now, there’s no point in me switchin’ in the kitchen.

m4p2mp3 – helper to turn an iTunes protected m4p to an mp3

m4p2mp3 — helper to turn an iTunes protected M4P to an MP3, so I can play music I have bought on my MP3 player. Probably runs best on a Unix-like OS.

You will need Perl, some M4Ps, mono, FairKeys, DeDRMS, faad, and lame. You’ll need to edit the script to say where the DeDRMS.exe file is. You’ll need to have run FairKeys to pick up your account details from Apple’s server.

Does the conversion via WAV, so you’ll definitely lose something. As written, MP3 file sizes are about 15% larger than the M4P. Doesn’t handle invalid MP3 genres gracefully at all; there is the beginnings of a mechanism to do this in the code, though.

This script doesn’t know anything about decryption, and thus contains no code to circumvent DRM.

livemp3 – convert those big old audio torrents to something listenable

You’ll need Perl, and Config::IniFiles.

Program: livemp3.

A sample ini file so you can see how to set this up: welch_rawlings_shepherds_bush.ini.

At the moment, this just generates output that you’ll need to feed to sh, but it handles renaming, converting and tagging MP3s to my satisfaction.

Update: it doesn’t handle FLAC tags, even though they’d be a good source of metadata. I may look into implementing that some day.

Ol’ Pointy-Nose Is Back …

Ben Hammersley’s Daily Doonesbury Feed, refactored:

#!/usr/bin/perl -w
use strict;
use integer;
use XML::RSS;

my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
  localtime(time);

my $this_year   = 1900 + $year;
my $todays_date = sprintf( "%02d%02d%02d", $this_year % 100, 1 + $mon, $mday );
my $db_url      =
    'http://images.ucomics.com/comics/db/'
  . $this_year . '/db'
  . $todays_date . '.gif';

my $rss = XML::RSS->new();
$rss->channel( title => "Doonesbury" );
$rss->add_item(
    title => 'Doonesbury for '
      . sprintf( "%05d/%02d/%02d", $this_year, 1 + $mon, $mday ),
    link        => $db_url,
    description => '<img src="' . $db_url . '" />'
);

print "Content-type: application/xml+rss\n\n", $rss->as_string;
exit;

An RSS generator for CBC Channels

This isn’t perfect (seems to fail on some feeds), but mostly works for me:


#!/usr/bin/perl -w
# cdf2rss - converts CBC KlipFarm CDF to crude RSS
# created by scruss on 02004/11/12
# RCS/CVS: $Id: cdf2rss,v 1.3 2004/11/13 03:59:21 scruss Exp $
# takes one argument, a stream name. Currently known streams are:
#
# Arts Business Calgary Canada
# Edmonton Montreal Ottawa
# Science Sports Toronto
# Vancouver Winnipeg World
#
# returns a crude RSS 1.0 stream fashioned from the CBC CDF output.
use strict;
use integer;
use XML::Simple;
use XML::RSS;
use LWP::Simple;
use constant CDFURL => 'http://www.cbc.ca/cdf/servlet/getCDF';
my $cdf = get( join( '?lineup=', CDFURL, $ENV{'QUERY_STRING'} ) );
my $xs = new XML::Simple;
my $ref = $xs->XMLin($cdf);
my $rss = new XML::RSS( version => '1.0' );
$rss->channel(
title => join( ' ', 'CBC', $ref->{category} ),
description => join( ' ', 'CBC', $ref->{category} ),
link => $ref->{href}
);
foreach my $cdf_item ( @{ $ref->{item} } ) {
my $tmp_abstract = $cdf_item->{abstract};
$tmp_abstract =~ s/\s+/ /g;
$tmp_abstract =~ s/^ //;
$tmp_abstract =~ s/ $//;
$rss->add_item(
title => $cdf_item->{title},
link => $cdf_item->{href},
description => $tmp_abstract
);
}
print "Content-type: application/xml+rss\n\n", $rss->as_string;
exit;

using MIME::Lite from ActiveState Perl on Windows

Wouldn’t you know it, but Windows just has to do things its own way. I’ve just started writing periodic system monitoring programs for our met station network, and needed to send e-mail. Under Unix, it was a simple matter of using MIME::Lite, and calling:

$msg->send();

But Windows doesn’t do sendmail, so you have to talk to the SMTP server directly:

$msg->send_by_smtp('your.SMTP.server.here');

That seems to work.

Poorly Hacked Perl

I have to admit, I’m gaining more than a sneaking admiration for PHP, the web application language that WordPress and Gallery are written in. It does remind me of an even more hacked-together version of Perl, but if it works well, well …

I looked for books, but they seemed to be out of date or very expensive, or both. So I’m sticking with the online PHP Manual.

nice scaling

My Nikon D70 makes images that are too large for the web, so I have to scale them down. Most image scaling routines use simple linear interpolation, which can lose a lot of detail, but some packages use cubic scaling. This keeps most of the detail.

I was looking for a scriptable cubic routine, and I found it in Image::Magick, aka perlmagick. The syntax is simple:

$x = $image->Resize(geometry => '50%',
                    filter => 'Cubic');

I used this routine to resize my 2004 Ontario Renfest pictures.

Getting my fortran head together

It’s very strange to be getting back into a language as different to Perl as it is possible to be. I’m fairly conversant with the weird bits of Perl — map, grep, hash usage, objects — but Fortran has a completely different toolkit

That’s not to say it’s a bad toolkit, just very different, F’rinstance, trying to find all the distinct values in an array. In Perl, you just walk through a hash, parallel to the array, incrementing each key for every value found. In Fortran — well, it’s a different story.

Touching the camel

Paul asked about getting back
to maintaining some Perl code after an absence of a few years. Since I
do a lot of Perl, here are some of the time-savers that I can’t live
without:

  • search.cpan.org allows you
    to search all the publicly-available modules on CPAN. There are few problems in Perl that
    haven’t been at least partially solved by a CPAN module. At the very
    least, make sure any web scripts use CGI.pm appropriately. I still see
    hand-rolled code that parses CGI arguments, never as well as CGI.pm would
    do.
  • PerlMonks is where you go
    to ask about your Perl problems, and find solutions. It’s worth
    learning a bit about the search options so you don’t ask a very old
    question again. This is me on
    PerlMonks, incidentally.
  • The Perl FAQ,
    included in the documentation as /perlfaq[1-9]?/. The Perl Cookbook is
    basically just the Perl FAQ on paper. Nice to hold, but you can’t
    search it the same way you can with perldoc -q <keyword>.

I would always advise Perl programmers to be
lazy
. Not slothful, but spend a little time seeing if someone
has solved your problem before. Thus you can turn many routine
programming jobs into a small matter of configuration.

I would also advise learning some of the idiomatic Perl tricks,
like ‘... or die ...‘, inline
if/unless, careful use of
undef, and list operators like map and
grep. It’s not just because you’re likely to meet them in
everyday code, but they’re very convenient. Once you start to miss
them in other languages, you’ll know that you are One Of
Us
.

Scripting Radio Buttons

I use Perl’s HTML::Template module a lot. It allows you to write web pages that are dynamically modified by the controlling Perl CGI/mod_perl application.

Most of my applications fill in forms from values in a database. This is easy enough when you are filling text fields, but if you ever use radio buttons, things kind of fall down.

I’ve found a way around this. Let’s say you have a status field that can have three values:

  • active
  • blocked
  • retired

So in Perl I define three constants:

  use constant STATUS_ACTIVE => 'active';
  use constant STATUS_BLOCK => 'block';
  use constant STATUS_RETIRE => 'retire';

Then in the template, I have something like this:

  <input type="radio" name="status"
   <!-- TMPL_IF NAME=STATUS_ACTIVE -->
   checked="checked"
   <!-- /TMPL_IF -->
  value="active" />Active

  <input type="radio" name="status"
   <!-- TMPL_IF NAME=STATUS_BLOCK -->
   checked="checked"
   <!-- /TMPL_IF -->

  value="block" />Blocked

  <input type="radio" name="status"
   <!-- TMPL_IF NAME=STATUS_RETIRE -->
   checked="checked"
   <!-- /TMPL_IF -->
  value="retire" />Retired

If the status variable is $account->status, say, I’d use:

  $template->param(
   STATUS_ACTIVE => ($account->status eq STATUS_ACTIVE),
   STATUS_BLOCK =>  ($account->status eq STATUS_BLOCK),
   STATUS_RETIRE => ($account->status eq STATUS_RETIRE)
  );

and, magically, the template picks up the right value.

If the status variable isn’t set to one of the three predefined values, you get a radio group that none of the values is selected. You might wish to think about how you’d deal with that, perhaps setting a safe default.

do me a favour

Don’t ever, ever nest ternary operators. Or at least, don’t do it in code I’m likely to see. Even if you think that ternary operators are the subject of wildlife TV documentaries, just don’t nest them. Okay?