<!-- --><style type="text/css">@import url(https://www.blogger.com/static/v1/v-css/navbar/3334278262-classic.css); div.b-mobile {display:none;} </style> </head><body><script type="text/javascript"> function setAttributeOnload(object, attribute, val) { if(window.addEventListener) { window.addEventListener('load', function(){ object[attribute] = val; }, false); } else { window.attachEvent('onload', function(){ object[attribute] = val; }); } } </script> <div id="navbar-iframe-container"></div> <script type="text/javascript" src="https://apis.google.com/js/plusone.js"></script> <script type="text/javascript"> gapi.load("gapi.iframes:gapi.iframes.style.bubble", function() { if (gapi.iframes && gapi.iframes.getContext) { gapi.iframes.getContext().openChild({ url: 'https://www.blogger.com/navbar.g?targetBlogID\x3d7256432\x26blogName\x3dThe+Frustrated+Programmer\x26publishMode\x3dPUBLISH_MODE_BLOGSPOT\x26navbarType\x3dBLACK\x26layoutType\x3dCLASSIC\x26searchRoot\x3dhttp://frustratedprogrammer.blogspot.com/search\x26blogLocale\x3den_US\x26v\x3d2\x26homepageUrl\x3dhttp://frustratedprogrammer.blogspot.com/\x26vt\x3d5012862196962223429', where: document.getElementById("navbar-iframe-container"), id: "navbar-iframe" }); } }); </script>
| Tuesday, December 26, 2006

UPDATE: Added more diagnostics to the code. If you were having issues, run it again and post the output to in a comment.
UPDATE2: Changed the second regular expression to be a little more loose. Hopefully this will resolve some of the problems.
UPDATE3: Blogger was removing some code that it thought was HTML causing an endless loop in the script. If you tried the script and it was just hanging, try it again.
UPDATE4: Fixed two things: One - Napster 4 changed its image naming conventions (Thanks Don). Two - This program never handled multi-byte characters well. Its somewhat hacked to fix this.

So, Napster isn't so good at album art for some reason -- no trace of it anywhere in my library. Funny thing is, Napster has its own stash of all the covers in C:\Documents and Settings\All Users\Application Data\Napster\image. Unfortunately its stored by some crazy obfuscated name like 12303503.jpg. Good news is that the Napster database, db30.bin, has the nessesary glue to piece things together. I hacked together some Perl to find my missing artwork and copy it to the album's directory as folder.jpg. This lets Windows Media Player tag the wma's correctly so they even transfer to my Zen with artwork.

Its a little ugly as you can see. I had to resort to some nasty regular expressions to track everything down without learning the entire file layout format. Please steal, improve, maintain, hack this as you see fit.

# This program will scan your music directory for missing album art (library). Then it will scan the napster
# database file (db) for a matching album. Then it will look up the missing image for that album in napsters
# own image archive (imgdir).
# Hopefully someone will come along and improve this, maybe make a plugin to WMP and improve the searching.

use File::Copy;

my $debug = 0;
my %hash = ();

$db = 'C:\Documents and Settings\All Users\Application Data\Napster\bin\db30.bin';
$imgdir = 'C:\Documents and Settings\All Users\Application Data\Napster\image';
#Your Napster library directory
$library = 'C:\Documents and Settings\CHANGEME\My Documents\My Music';
#Your Napster Username
$username = 'CHANGEME';

print "Fixing Napster Album Art v1.3\n";

# Find the albums with missing art
print "Searching for missing album art:\n";
opendir(LIB, $library) or die "Can't open $library: $!";
while( defined ($artist = readdir LIB) ) {
next if $artist =~ /^\.\.?$/; # skip . and ..

opendir( ART, "$library/$artist" );
while( defined ($album = readdir ART) ) {
next if $album =~ /^\.\.?$/; # skip . and ..
if ( -d "$library/$artist/$album" ) {
if( ! -f "$library/$artist/$album/folder.jpg" ) {
#get the first song in the album
opendir( ALB, "$library/$artist/$album" );
while( defined ($song = readdir ALB) ) {
next if $song =~ /desktop.ini/;

if( -f "$library/$artist/$album/$song" ) {
$hash{$songReg}{'missing'} = 'true';
$hash{$songReg}{'album'} = $album;
$hash{$songReg}{'artist'} = $artist;
print " - Missing album art for $artist - $album\n";

$size = keys( %hash );
print "Missing album art for $size albums\n";
if( $size == 0 ) {
exit 0;

# Find a song in that album
print "\nSearching for missing albums in napster database\n";
open(DB, $db) or die "Can't open $db: $!";
while(<DB>) {
while(m/\000$username\000(\w+)\0001\0000\0001\000\w+\000([^\000]+)\000([^\000]+)\000/g) {
$songID = $1;
$songName = $2;
$songFileName = $3;
#Normalize non-printable characters to periods

if( exists $hash{$songReg} ) {
print " - Found missing albums $hash{$songReg}{'album'} - first songID: $songID\n";
$hash{$songReg}{'ID'} = $songID;

# Find the album ID of that song
print "\nSearching for albumID and artwork in napster directories\n";
open(DB, $db);
while(<DB>) {
while(m/\000$username\000(\d+)\000(\d{5,20})\000(\d{5,20})\000([^\000]+)\000/g) {
my $songID = $1;
my $artistID = $2;
my $albumID = $3;
my $songShortName = $4;

for my $song ( keys %hash ) {
if( $hash{$song}{'missing'} eq 'true' && $hash{$song}{'ID'} eq $songID ) {
$dir = "$library/$hash{$song}{'artist'}/$hash{$song}{'album'}";
$img = "$imgdir"."/"."$albumID".".jpg";
print " - Found albumID for $dir: $albumID\n";

if( -f $img ) {
print " - Found artwork for album: $img\n";
#Finally copy the image for that album to its new home
copy( $img, "$dir/folder.jpg");
$hash{$song}{'missing'} = 'false';

} else {
print " - Error missing album image for $hash{$song}{'album'}: $albumID\n";
print " - img is: $img\n";

Steps to use this program:
1-Download and install perl from http://www.perl.com
2-Save that script to a file something like fixNapsterArt.pl
3-Edit fixNapsterArt.pl changing the CHANGEME lines to the appropriate values
4-Run fixNapsterArt.pl