Recently I posted a little program to implement a watched folder/renamer for newly downloaded mp3 files. More recently, eMusic.com went and changed things around a little on their system. Now the mp3 links are ill-named redirects, and they don't play nicely with GetRight anymore, since GetRight wants to create the file before actually checking out the URL and getting the real server-suggested filename.
BUT! One cool thing about eMusic is the RMP link for each album. It allows you to download the full album, provided your player is Zinf.
Or RealPlayer I think, but puh-leeze, yucko.
The thing is, I dig winamp. I don't wanna switch. So I looked at these .rmp files, and saw that they're just XML. A little further googling turned up a groovy java-based utility for RMP files, but that had one problem: It wasn't in perl. So I rolled up my sleeves, found a spec-let for RMP, and wrote a nice little CL-based RMP download utility. It allows you to customize the resulting file names, as well as the option of creating directories for artist/album. I wrote it on windows, but it ought to work in Linux. I'll try that when I can.
I have not done much with XML::* or LWP before, so it was a great learning experience on both fronts. I'm seeking input on the code itself, style, concept, whatever.
the code follows:
#!/usr/bin/perl -w
=head1 NAME
getrmp.pl
=head1 SYNOPSIS
Pass it a local filename:
getrmp.pl /path/to/somefile.rmp
getrmp.pl C:\path\to\somefile.rmp
Or a URL:
getrmp.pl http://server.com/path/to/somefile.rmp
NOTE that URL support is primitive, if the server needs cookies,
authentication, or redirects, the program will die.
=head1 ABSTRACT
RMP is an XML format for playlists, begun initially by RealNetworks I
+think.
eMusic.com uses it to allow customers to download full albums with one
+ click.
Freeamp/zinf support RMP, but WinAMP does not. And since eMusic.com ch
+anged
things around recently, it's harder to use dowload managers because th
+e mp3
links are given broken names and then redirected. My mouse was getting
+ tired
of clicking each song, so this is quick and dirty an RMP processor tha
+t, when
passed an RMP file, will download the album and save it somewhere.
Be sure to look over the configuration section for customizations.
Of course all corporate names used herein are trademarked and copyrigh
+ted by
their respective owners. And this software is free, with no warranty.
+Yeah.
=cut
use XML::Simple;
use LWP::UserAgent;
use LWP::Simple;
use File::Copy;
use strict;
## CONFIGURATION
## Where to put files
my $dlpath = "d:\\music\\";
## Make directories for each artist?
my $artistdir = 1;
## Make sub-artist directories for each album?
my $albumdir = 0;
## How to name the new music files.
my $naming = '[##ARTIST## - ##ALBUM##] ##TRACK## ##TITLE##';
### Other configs
## User agent to report
my $agent = 'Mozilla/8.0';
### END OF CONFIGURATION
my $ua = LWP::UserAgent->new;
$ua->agent($agent);
my $rmpfilename = $ARGV[0] || die q|
Usage:
getrmp.pl path/or/URL/to/file.rmp
getrmp.pl C:\path\to\somefile.rmp
getrmp.pl http://server.com/path/to/somefile.rmp
|;
my $rmpfile;
#### This IF construct may not work if it needs authentication,
#### cookie, or redirect support.
if ($rmpfilename =~ m|^\w+://|) { # we've got a URL
print "Getting rmp file: ";
my $res = $ua->request( HTTP::Request->new(GET=>$rmpfilename) );
# Check the outcome of the response
if ($res->is_success) {
$rmpfile = $res->content;
print "OK\n";
} else {
die "Can't get remote $rmpfilename";
}
} else { #it's a regular /path/to/file on disk
$rmpfile = $rmpfilename;
}
my $xmlref = XMLin($rmpfile) || die "Something bad happened with XML::
+Simple";
my $baseurl = 'http://'.
$xmlref->{SERVER}{NETNAME}.
$xmlref->{SERVER}{LOCATION};
# got these from http://docs.real.com/docs/rn/realone/RMP_Creation_Gui
+de.pdf
my %parsecodes = (
fid => 'TRACKID',
f => 'FILENAME',
# Note, emusic.com does not use these two, and I don't know what they
+are. see above URL.
lid => 'UNUSED',
pid => 'UNUSED',
);
my $total = scalar @{$xmlref->{TRACKLIST}{TRACK}};
my $tcount;
print "Downloading '$xmlref->{TITLE}' from $xmlref->{SERVER}{NETNAME}\
+n\n";
foreach my $track (@{$xmlref->{TRACKLIST}{TRACK}}) {
# Weed out some bad characters
for (qw(ARTIST ALBUM TITLE)) {
$track->{$_} =~ s([\*\?\/\\])(-)g;
}
my $TRACKNO = sprintf("%02d",++$tcount);
# Here's where we make any new dirs
if ($artistdir) {
makedir($dlpath.$track->{ARTIST})
or die "Can't make directory $dlpath.$track->{ARTIST}";
if ($albumdir) {
makedir($dlpath.$track->{ARTIST}."\\".$track->{ALBUM})
or die "Can't make directory $dlpath.$track->{ARTIST}.
+$track->{ALBUM}";
}
}
# Figure out the new filename
(my $newfile = $naming) =~ s/##TRACK##/$TRACKNO/g;
for (qw(ARTIST ALBUM TITLE)) {
$newfile =~ s/##$_##/$track->{$_}/ge;
}
my $fullpath = $dlpath .
($artistdir ? "$track->{ARTIST}\\" : '') .
($albumdir ? "$track->{ALBUM}\\" : '') .
$newfile;
my ($expected_length, $bytes_received);
open FH, "> $fullpath.getrmp" or die "Can't open file $fullpath.ge
+trmp";
binmode (FH);
print "$tcount/$total: $newfile\n";
my $url = $baseurl;
# Interpolate the path codes
for (qw(fid f lid pid)) {
$url =~ s/\%$_/$track->{ $parsecodes{$_} }/g;
}
my ($extension) = ($track->{FILENAME} =~ /(\.\S*)$/);
my $tracktime = time;
my $res = $ua->request( HTTP::Request->new(GET => $url),
sub {
my($chunk, $res) = @_;
$bytes_received += length($chunk);
unless (defined $expected_length) {
$expected_length = $res->content_length || 0;
}
print FH $chunk;
progress($bytes_received, $expected_length);
},1024);
close FH;
my $ttime = time - $tracktime;
my $kbps = int ($bytes_received / 1024 / $ttime);
print " - $bytes_received bytes OK at $kbps KBps\n";
move("$fullpath.getrmp", $fullpath.$extension)
or print " ERROR Moving File. Temp file left.\n";
} #END foreach
print "Finished\n";
sub progress {
my ($val, $tot) = @_;
printf("\b" x 4 . "%3d%%", int(100 * $val / $tot));
}
sub makedir {
my $dir = shift;
if (-e $dir) {
return 1;
}else{
return mkdir $dir;
}
}
Updated with <READMORE> tag.
cheers!
-
Are you posting in the right place? Check out Where do I post X? to know for sure.
-
Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
<code> <a> <b> <big>
<blockquote> <br /> <dd>
<dl> <dt> <em> <font>
<h1> <h2> <h3> <h4>
<h5> <h6> <hr /> <i>
<li> <nbsp> <ol> <p>
<small> <strike> <strong>
<sub> <sup> <table>
<td> <th> <tr> <tt>
<u> <ul>
-
Snippets of code should be wrapped in
<code> tags not
<pre> tags. In fact, <pre>
tags should generally be avoided. If they must
be used, extreme care should be
taken to ensure that their contents do not
have long lines (<70 chars), in order to prevent
horizontal scrolling (and possible janitor
intervention).
-
Want more info? How to link
or How to display code and escape characters
are good places to start.