#!/usr/bin/perl
#
# Parts of this script use knowledge obtained from "ExtractSelectedArtwork.js",
# a sample JScript application included with the iTunes COM for Windows SDK.
# 
use strict;
#
# Modules we'll need:
#
use Win32::OLE;
use Win32::OLE::Const 'iTunes';
use File::Basename;
fileparse_set_fstype("MSWin32");
#
# Try to open a COM interface to a running iTunes first, then create an
# instance if it isn't already running:
#
my $iTunesApp;
eval {$iTunesApp = Win32::OLE->GetActiveObject('iTunes.Application')};
die "iTunes does not appear to be installed on this system.\n" if $@;
if (!defined($iTunesApp)) {
    print STDERR "Starting new instance of iTunes ... ";
    $iTunesApp = new Win32::OLE 'iTunes.Application', 'Quit'
              or die "Cannot start iTunes!\n";
    print STDERR "Version " . $iTunesApp->Version . " started.\n";
    #$iTunesApp->{'visible'} = 0; # Hide iTunes from view
} else {
    print STDERR "Attached to current instance of iTunes, version " .
                 $iTunesApp->Version . ") ...\n";
}
#
# Get the library playlist and an object containing every track in it:
#
my $mainlibrary = $iTunesApp->LibraryPlaylist;
my $tracks      = $mainlibrary->Tracks;
#
# If we didn't get a good track list, there's no point in going
# any further:
#
die "Could not get track list.\n" if (!defined($tracks));
#
# A couple of hashes we'll be filling in along the way (or, if our
# library is in good shape, not filling in):
#
my %noart; my %multart; my %unkart; my %newart;
#
# Now we'll go through the tracks, one at a time:
#
print STDERR "Now scanning " . $tracks->Count . " songs .";
foreach my $trackno (1..$tracks->Count) {
    #
    # Print a status indicator to show that something's happening:
    #
    if (!($trackno % 1000)) {
        print STDERR ":";
    } elsif (!($trackno % 100)) {
        print STDERR ".";
    }
    #
    # Get the current track:
    #
    my $currtrack = $tracks->Item($trackno);
    #
    # If no info is found, it's probably a "dead" track:
    #
    if (!defined($currtrack)) {
        print "No track defined for number $trackno.\n";
        next;
    }
    #
    # Get to the cover art object for the current track:
    #
    my $currartworks = $currtrack->Artwork;
    #
    # Here's where we'll hold the extender for the artwork type:
    #
    my $ext;
    #
    # We only want one piece of cover art for each track, and we'll
    # Keep track of those that have less or more than that. We'll
    # also not art files that aren't any of the known formats:
    #
    if ($currartworks->Count == 0) {
        push(@{$noart{$currtrack->Album}}, $currtrack->Name);
    } elsif ($currartworks->Count > 1) {
        push(@{$multart{$currtrack->Album}}, $currtrack->Name);
    } elsif (($ext = artext($currartworks->Item(1)->Format)) eq ".unk") {
        push(@{$unkart{$currtrack->Album}}, 
              $currtrack->Artist . "/" . $currtrack->Album . "/" .
              $currtrack->Name .  " has an unusual extender of " .
              artext($currartworks->Item(1)->Format) . ".\n");
    } else {
        my $directory = dirname($currtrack->Location);
        my $artfile = "$directory\\Folder.jpg";
        if (! -f $artfile) {
            push(@{$newart{$currtrack->Album}}, $directory);
            $currartworks->Item(1)->SaveArtworkToFile($artfile);
        }
    }
}
#
# Terminate our line of dots:
#
print STDERR "\n";
#
# Report on what we found:
#
if (scalar(%noart)) {
    print "\n:::: Tracks Missing Artwork ::::\n\n";
    my $curalb;
    foreach my $album (sort keys %noart) {
        if ($album ne $curalb) {
            $curalb = $album;
            print "$album:\n";
        }
        foreach my $track (@{$noart{$album}}) {
            print "\t$track\n";
        }
    }
}

if (scalar(%multart)) {
    print "\n:::: Tracks With Multiple Artwork ::::\n\n";
    my $curalb;
    foreach my $album (sort keys %multart) {
        if ($album ne $curalb) {
            $curalb = $album;
            print "$album:\n";
        }
        foreach my $track (@{$multart{$album}}) {
            print "\t$track\n";
        }
    }
}

if (scalar(%unkart)) {
    print "\n:::: Tracks With Artwork Files of Unknown Type ::::\n\n";
    my $curalb;
    foreach my $album (sort keys %unkart) {
        if ($album ne $curalb) {
            $curalb = $album;
            print "$album:\n";
        }
        foreach my $track (@{$unkart{$album}}) {
            print "\t$track\n";
        }
    }
}

if (scalar(%newart)) {
    print "\n:::: Artwork Files Created from Song Files ::::\n\n";
    my $curalb;
    foreach my $album (sort keys %newart) {
        if ($album ne $curalb) {
            $curalb = $album;
            print "\t$album.\n";
        }
    }
}

#
######################################################################
#
# Subroutines:
#
# This subroutine is based on a function named ArtworkFormatToFileExtension()
# from "ExtractSelectedArtwork.js", but with a much shorter name.
#
sub
artext($)
{
    my $artworkformat = shift;

    ($artworkformat == 1) && return ".jpg";
    ($artworkformat == 2) && return ".png";
    ($artworkformat == 3) && return ".bmp";
    return ".unk";
}
