############################################################################### # # itunes_preferences_fix.pl # # This script will restore the setting in iTunes so that it will ask for # confirmation when deleting a non-empty playlist # # Copyright (C) 2007 Robert Jacobson # written by: Robert Jacobson (http://mysite.verizon.net/teridon/itunesscripts) # Last Updated: 7 Jan 2008 # Version 1.3 # # This script is GPL v2. see http://www.gnu.org/copyleft/gpl.html # # 1.2 Updated for iTunes 7.4 (tested with 7.4.2.4) # 1.3 Updated for iTunes 7.5.0.20 # ################################################################################ use File::Basename; my $PROGNAME = basename($0); my $VERSION = "1.2"; my $AUTHOR = "Robert Jacobson"; my $HOMEPAGE = "http://mysite.verizon.net/teridon/"; my $YEAR = 2007; my $GNU_URL = "http://www.gnu.org/copyleft/gpl.html"; { print "**************************************************************\n" . "$PROGNAME version $VERSION, Copyright (C) $YEAR $AUTHOR\n" . "Visit $HOMEPAGE for updates\n" . "$PROGNAME comes with ABSOLUTELY NO WARRANTY;\n". "This is free software, and you are welcome\n" . "to redistribute it under certain conditions\n" . "for details see $GNU_URL.\n" . "**************************************************************\n" . "\n" ; } use MIME::Base64; use XML::Simple; use Data::Dumper; use warnings; use strict; use Win32::OLE; # Create a signal handler to destroy the iTunes object # in case our program quits before the end use sigtrap 'handler', \&quit, 'normal-signals'; ## Create the OLE Object my $iTunes = Win32::OLE->new('iTunes.Application') or die Win32::OLE->LastError(); # Check version first! my $version = $iTunes->Version; if ($version !~ /^(7.0|7.1.1.5|7.2.0.35|7.4.*|7.5.0.20)/) { print "Sorry, this script requires iTunes 7.0, 7.1.1.5, 7.2.0.35, 7.4.*, 7.5.0.20\n"; quit(); } else { print "iTunes Version $version\n"; } # Exit iTunes and Wait for iTunes process to go away my $shell = Win32::OLE->new('WScript.Shell') or die Win32::OLE->LastError(); my $WMI = Win32::OLE->GetObject("winmgmts:{impersonationLevel=impersonate}!\\\\.\\root\\cimv2") or die Win32::OLE->LastError; my $query = $WMI->ExecQuery("Select * from Win32_Process Where Name = 'iTunes.exe'"); for my $item (in $query) { my $name = (split(/\./,$item->Name()))[0]; $shell->AppActivate($name); $shell->SendKeys("%(fx)"); } while ($query->Count){ print "Waiting for iTunes to finish...\n"; sleep 1; $query = $WMI->ExecQuery("Select * from Win32_Process Where Name = 'iTunes.exe'"); } my $appdir = $ENV{'APPDATA'} . '\Apple Computer\iTunes'; my ($magic_key, $magic_number_playlist, $magic_number_song); # "Magic" values depend on iTunes version if ($version eq "7.1.1.5" or $version eq "7.2.0.35" or $version =~ /7.4.*/ or $version eq "7.5.0.20") { # The name of the key which stores the preference # For some dumb reason this moved from pref 129 to 130 in 7.1 $magic_key = "Preferences:130"; # The offset into the Base64-encoded data of the bit for whether to ask # when deleting a song or playlist $magic_number_song = "2495"; $magic_number_playlist = "1938"; } elsif ($version =~ /^7.0/) { $magic_key = "Preferences:129"; $magic_number_song = "2495"; $magic_number_playlist = "1938"; # I don't know the magic number for iTunes 7.0 } else { print "Sorry, this version has not been tested with version $version\n"; print "Better safe than sorry! Exiting...\n"; sleep 5; quit() } my $file = $appdir . '\iTunesPrefs.xml'; my $xs = XML::Simple->new( NormaliseSpace => 2, ); my $xml = $xs->XMLin($file) or die "could not find iTunesPrefs file: $!"; for my $dict ( @{$xml->{dict}->{dict}} ) { my $key = $dict->{key}; my $data = $dict->{data}; if (ref($key) eq 'ARRAY') { #print "sub:\n"; my $i = 0; my $foundkey = 0; foreach my $subkey (@{$key}) { #print "$subkey\n"; my $value = ${$data}[$i]; $value =~ s/\s+//g; #print "$value\n"; my $decode = MIME::Base64::decode($value); if ($subkey =~ /$magic_key/) { $foundkey = 1; #print "$value\n"; #open OUT,">output.b64" or die "$!"; #print OUT $decode; # Get do not ask value my $dontask_song = unpack("C", substr($decode, $magic_number_song, 1)); my $dontask_playlist = unpack("C", substr($decode, $magic_number_playlist, 1)); #print MIME::Base64::encode_base64( $decode); printf("dontask for song is %d : iTunes %s when deleting\n", $dontask_song, $dontask_song ? "will not ask" : "already set to ask"); printf("dontask for playlist is %d : iTunes %s when deleting\n", $dontask_playlist, $dontask_playlist ? "will not ask" : "already set to ask"); if ($dontask_song or $dontask_playlist) { print "Exiting iTunes... please click the Quit button if asked\n"; # Exit iTunes if it is running my $shell = Win32::OLE->new('WScript.Shell') or die Win32::OLE->LastError(); my $WMI = Win32::OLE->GetObject("winmgmts:{impersonationLevel=impersonate}!\\\\.\\root\\cimv2") or die Win32::OLE->LastError; my $query = $WMI->ExecQuery("Select * from Win32_Process Where Name = 'iTunes.exe'"); # ExecQuery returns a Win32 Object list (swbemobjectset), # which must be Enumerated. my @list = Win32::OLE::Enum->All($query); $iTunes->Quit; # for my $item (@list) { # my $name = (split(/\./,$item->Name()))[0]; # $shell->AppActivate($name); # $shell->SendKeys("%(fx)"); # } # Make sure iTunes is gone before continuing while ($query->Count){ sleep 1; $query = $WMI->ExecQuery("Select * from Win32_Process Where Name = 'iTunes.exe'"); } print "Changing dontask to 0 so iTunes will ask again...\n"; my $hex; # "uncheck" the "do not ask again" box (set dontask to 0) # Changes both for song and playlist substr($decode, $magic_number_playlist, 1) = pack("C", 0); substr($decode, $magic_number_song, 1) = pack("C", 0); for (my $offset = 0; $offset <= length($decode) ; $offset += 45) { #print MIME::Base64::encode_base64( substr($decode, $offset, 45) ); $hex .= MIME::Base64::encode_base64( substr($decode, $offset, 45) ); } &replace($hex); } else { print "No changes to preferences made\n"; } } $i++; } if (not $foundkey) { print "ERROR! Could not find User Preferences key!\n"; } } else { #print "$key\n"; #$data =~ s/ //g; #print MIME::Base64::decode($data) . "\n"; #print "$data\n"; } } print "\n"; #print OUT MIME::Base64::decode($ARGV[0]); # Destroy the object. Otherwise zombie object will come back # to haunt you #quit(); sub quit { # This destroys the object undef $iTunes; exit; } sub replace { my $hex = shift; undef($/); open (IN, $file) or die "could not open $file : $!"; my $data = ; close(IN); # Replace newlines with newlines plus two tabs $hex = "\t\t" . $hex; $hex =~ s#\n#\n\t\t#gs ; $data =~ s#${magic_key}\s+\s+(.*?)#${magic_key}\n\t\t\n$hex#s ; my $bak = $file . $$ . ".bak"; print "Your old preferences file saved as $bak\n"; rename($file, $bak) or die "Could not rename current prefs: $!"; open NEW, ">$file"; print NEW $data; close NEW; }