Coverlover - A PDF wallpaper showing random iTunes cover art

Initially I developed this script for a Majorcan disco owner who wanted to show cover art with a video projector, but after finishing Lacurne I slightly modified it to create and set a customizable PDF wallpaper on your desktop that shows random iTunes cover art.

Once again, the raw image and PDF processing is done with Python and its Quartz 2D bindings while the GUI, Finder interaction and preferences management is handled by AppleScript. Together this makes for a very efficient and convenient Mac automation solution.

The script allows to adjust the maximum cover width and height, let’s you set a foreground color and a color tint and can reduce the file size of the created PDF wallpaper by applying a Quartz filter.

Of course I invite you to download and inspect this free little AppleScript, but please don’t expect a polished application this time :wink: It’s still beta software (nevertheless it works just fine on all my private and company Macs):

Coverlover - A customizable PDF wallpaper showing random iTunes cover art on your desktop (v0.2, ca. 114.7 KB)

Coverlover requires Mac OS X 10.5 Leopard and iTunes 7. It was successfully tested on Intel and PowerPC based Macs, but runs significantly faster on Intel Macs.

To create and set a PDF wallpaper just open the script with a double click. If you want to open the configuration menu, then just drag and drop a Finder item onto the script’s icon:

Please note, that you won’t instantly see the changes made in the configuration menu. The new settings will only show up after you run Coverlover once again, as it will then create and set a new PDF document according to the chosen options.

Coverlover currently assumes that your iTunes folder containing ITC cover art files is located here:

~/Music/iTunes/Album Artwork/

The preferences are currently saved inside Coverlover’s script bundle.

If you are interested in the Python code that generates the PDF wallpaper, you can study it right here.

Important: Opening and saving the below script code in Script Editor won’t result in a usable AppleScript! That is because Coverlover internally relies on several scripts, which are located inside its Script bundle. Therefor please download the complete script here.


-- created: 07.06.2008
-- version: 0.1
-- requires:
-- ¢ Mac OS X 10.5 Leopard & iTunes 7
-- tested on:
-- ¢ Intel & PowerPC based Macs
-- notes:
-- ¢ runs a lot faster on an Intel based Mac
-- ¢ dual monitor setups are currently not supported

-- This AppleScript creates and sets a PDF wallpaper made of random iTunes cover art according to the chosen options.
-- To enter the configuration menu, just drop Finder items onto the script's icon.

property mytitle : "Coverlover"

-- I am called when the user drops Finder items onto the script's icon.
-- I am displaying the configuration menu and managing the preferences.
on open finderitems
	repeat
		set chosenmenuitem to my dspmainmenu()
		if chosenmenuitem begins with "Set cover width" then
			set coverwidth to my askforcoversize("width")
			if coverwidth is not missing value then
				my savepref("coverwidth", coverwidth)
			end if
		else if chosenmenuitem begins with "Set cover height" then
			set coverheight to my askforcoversize("height")
			if coverheight is not missing value then
				my savepref("coverheight", coverheight)
			end if
		else if chosenmenuitem is "Set foreground color" then
			set fgcolor to my askforfgcolor()
			if fgcolor is not missing value then
				my savepref("fgcolor", fgcolor)
			end if
		else if chosenmenuitem is "Deactivate foreground color" then
			my savepref("fgcolor", missing value)
		else if chosenmenuitem begins with "Set foreground color tint" then
			set colortint to my askforcolortint()
			if colortint is not missing value then
				my savepref("colortint", colortint)
			end if
		else if chosenmenuitem is "Enable file size reduction" then
			my savepref("quartzfilter", true)
		else if chosenmenuitem is "Disable file size reduction" then
			my savepref("quartzfilter", false)
		else if chosenmenuitem is missing value then
			exit repeat
		end if
	end repeat
end open

-- I am called when the user opens the script with a double click on its icon.
-- I am trying to create the wallpaper made of iTunes cover art.
on run
	try
		-- loading the preferences script object
		set prefs to my loadprefs()
		if prefs is missing value then
			set errmsg to "Could not load the preferences script located inside my script bundle."
			my dsperrmsg(errmsg, "--")
			return
		end if
		-- preparing the preferences, so that they can be passed as
		-- string arguments to the Python script
		set coverwidth to (prefs's coverwidth) as Unicode text
		set coverheight to (prefs's coverheight) as Unicode text
		set fgcolor to prefs's fgcolor
		if fgcolor is missing value then
			set fgcolor to quoted form of "missing value"
		else
			set {redval, greenval, blueval} to prefs's fgcolor
			-- replacing commas with points in float values, as
			-- Python only accepts a point as decimal separator
			set redval to my searchnreplace(",", ".", redval as Unicode text)
			set greenval to my searchnreplace(",", ".", greenval as Unicode text)
			set blueval to my searchnreplace(",", ".", blueval as Unicode text)
			set fgcolor to redval & space & greenval & space & blueval
		end if
		set colortint to my searchnreplace(",", ".", (prefs's colortint as Unicode text))
		set quartzfilter to (prefs's quartzfilter) as Unicode text
		-- creating the shell command calling the Python script with the
		-- corresponding arguments
		set pyscriptpath to (((path to me) as Unicode text) & "Contents:Resources:Scripts:coverlover.py")
		set command to "/usr/bin/python " & quoted form of POSIX path of pyscriptpath & " " & coverwidth & " " & coverheight & " " & fgcolor & " " & colortint & " " & quartzfilter
		set shellresult to paragraphs of (do shell script command)
		-- maybe there was an error...
		if item 1 of shellresult begins with "Error:" then
			set errmsg to ((characters 8 through -1 of (item 1 of shellresult)) as Unicode text)
			my dsperrmsg(errmsg, "--")
			return
		end if
		-- PDF wallpaper successfully created?
		set pdffilepath to (((path to application support folder from user domain) as Unicode text) & "Coverlover:coverlover.pdf")
		if not itempathexists(pdffilepath) then
			set errmsg to "The PDF wallpaper could not be created."
			my dsperrmsg(errmsg, "--")
			return
		end if
		-- file path to the PNG image used as an update helper: if you try to update the desktop picture and
		-- the file path of your new image is the same as the current one, you won't see
		-- the new image. therefor we first «switch» to the small PNG image before updating
		-- the wallpaper. 
		set switchfilepath to (((path to me) as Unicode text) & "Contents:Resources:switch.png")
		tell application "Finder"
			set desktop picture to (switchfilepath as alias)
		end tell
		delay 0.25
		tell application "Finder"
			set desktop picture to (pdffilepath as alias)
		end tell
		-- catching unexpected errors
	on error errmsg number errnum
		my dsperrmsg(errmsg, errnum)
	end try
end run

-- I am displaying the configuration menu and return the chosen menu item.
-- In case the user cancels I return «missing value».
on dspmainmenu()
	-- loading the preferences script object
	set prefs to my loadprefs()
	if prefs is missing value then
		set errmsg to "Could not load the preferences script located inside my script bundle."
		my dsperrmsg(errmsg, "--")
		return missing value
	end if
	set micoverwidth to "Set cover width (" & prefs's coverwidth & "px)"
	set micoverheight to "Set cover height (" & prefs's coverheight & "px)"
	set micolortint to "Set foreground color tint (" & prefs's colortint & ")"
	set menuitems to {micoverwidth, micoverheight, "Set foreground color", "Deactivate foreground color", micolortint, "Enable file size reduction", "Disable file size reduction"}
	choose from list menuitems with title mytitle OK button name "Select" cancel button name "Quit" with prompt "Please choose an option" without multiple selections allowed and empty selection allowed
	set usrchoice to result
	if usrchoice is not false then
		return item 1 of (usrchoice as list)
	else
		return missing value
	end if
end dspmainmenu

-- I am loading the preferences script object and return it.
-- In case of an error I return «missing value».
on loadprefs()
	set prefsfilepath to (((path to me) as Unicode text) & "Contents:Resources:Scripts:prefs.scpt")
	try
		set prefs to load script (prefsfilepath as alias)
		return prefs
	on error
		return missing value
	end try
end loadprefs

-- I am saving the chosen preferences to a script file
on savepref(prefkey, prefvalue)
	set prefsfilepath to (((path to me) as Unicode text) & "Contents:Resources:Scripts:prefs.scpt")
	set prefs to load script (prefsfilepath as alias)
	if prefkey is "coverwidth" then
		set prefs's coverwidth to prefvalue
	else if prefkey is "coverheight" then
		set prefs's coverheight to prefvalue
	else if prefkey is "fgcolor" then
		set prefs's fgcolor to prefvalue
	else if prefkey is "colortint" then
		set prefs's colortint to prefvalue
	else if prefkey is "quartzfilter" then
		set prefs's quartzfilter to prefvalue
	end if
	store script prefs in (prefsfilepath as alias) replacing yes
end savepref

-- I am asking the user to provide the maximum cover width/height and return the integer value.
-- In case the user cancels I return «missing value».
on askforcoversize(mode)
	set dlgmsg to "Please enter the maximum cover " & mode & " as an integer:"
	try
		display dialog dlgmsg default answer "" buttons {"Cancel", "Enter"} default button 2 with title mytitle
	on error
		-- User canceled
		return missing value
	end try
	set dlgresult to result
	set usrinput to text returned of dlgresult
	-- the user did not enter anything...
	if usrinput is "" then
		my askforcoversize(mode)
	else
		-- let's check if the user entered numbers only
		set nums to {"1", "2", "3", "4", "5", "6", "7", "8", "9", "0"}
		set invalidchars to ""
		repeat with char in usrinput
			if char is not in nums then
				set invalidchars to invalidchars & char & space
			end if
		end repeat
		-- we found invalid characters
		if invalidchars is not "" then
			set errmsg to "We found the following characters in the given input. Please enter numbers only." & return & return & invalidchars & return
			my dsperrmsg(errmsg, "--")
			my askforcoversize(mode)
		else
			-- let's try to transform the user input into an integer
			try
				set intvalue to usrinput as integer
				return intvalue
			on error
				set errmsg to "We could not coerce the given input into an integer:" & return & return & usrinput & return
				my dsperrmsg(errmsg, "--")
				my askforcoversize(mode)
			end try
		end if
	end if
end askforcoversize

-- I am asking the user to choose a color.
-- In case the user cancels I return «missing value».
on askforfgcolor()
	try
		set chosencolor to choose color
	on error
		return missing value
	end try
	set {redval, greenval, blueval} to chosencolor
	set redval to redval / 65535
	set greenval to greenval / 65535
	set blueval to blueval / 65535
	return {redval, greenval, blueval}
end askforfgcolor

-- I am asking the user to choose a tint value to highlight the last and next month
on askforcolortint()
	set decseparator to my getdecseparator()
	set dlgmsg to "Please choose a tint value used for the foreground color:" & return & "(value must be between 0 and 1," & return & "decimal separator: «" & decseparator & "»)"
	try
		tell me
			activate
			display dialog dlgmsg default answer "" buttons {"Cancel", "Enter"} default button 2 with title mytitle
		end tell
		set dlgresult to result
	on error
		return missing value
	end try
	set usrinput to text returned of dlgresult
	if usrinput is "" then
		my askforcolortint()
	else
		set usrinputisnum to true
		try
			set usrinput to usrinput as number
		on error
			set usrinputisnum to false
		end try
		if usrinputisnum is false then
			set errmsg to "The entered value could not be coerced into a number."
			my dsperrmsg(errmsg, "--")
			my askforcolortint()
		else
			if usrinput < 0 or usrinput > 1 then
				set errmsg to "The entered value is less than 0 or greater than 1."
				my dsperrmsg(errmsg, "--")
				my askforcolortint()
			else
				return usrinput
			end if
		end if
	end if
end askforcolortint

-- I am returning the currently used decimal separator
on getdecseparator()
	set strnum to 0.1 as Unicode text
	set decseparator to character 2 of strnum
	return decseparator
end getdecseparator

-- I am a very old search & replace function...
on searchnreplace(searchstr, replacestr, txt)
	considering case, diacriticals and punctuation
		if txt contains searchstr then
			set olddelims to AppleScript's text item delimiters
			set AppleScript's text item delimiters to {searchstr}
			set txtitems to text items of txt
			set AppleScript's text item delimiters to {replacestr}
			set txt to txtitems as Unicode text
			set AppleScript's text item delimiters to olddelims
		end if
	end considering
	return txt
end searchnreplace

-- I am indicating if a given item path exists or not
on itempathexists(itempath)
	try
		set itemalias to itempath as alias
		return true
	on error
		return false
	end try
end itempathexists

-- I am displaying error messages to the user
on dsperrmsg(errmsg, errnum)
	
	tell me
		activate
		display dialog "Sorry, an error occured:" & return & return & errmsg & return & "(" & errnum & ")" buttons {"OK"} default button 1 with icon stop giving up after 20 with title mytitle
	end tell
end dsperrmsg