The amazing AppCam!

Some time ago I published an article on my website describing an imaginary AppleScript named AppCam that would work like a webcam for the applications currently running on your Mac. Many people like to tell the world what they are currently listening to in iTunes and so I thought: Why not showing the world, which applications are currently running in your Dock?

Back then I just roughly described the idea, but did not post any working AppleScript code. When I recently reviewed my article I instantly realized that I cannot just reformat and publish it here on Code Exchange. Now being a member of MacScripter I feel the responsibility to deliver substantial content that meets the high expectations of the valued MacScripter community :wink:

And so this week I sacrificed all my lunch times in order to write a stable & well working prototype of the amazing AppCam! I invite you to download and inspect it on your fine Mac:

The amazing AppCam (version 0.1, ca. 79.4 KB)

An AppCam image (32px) with horizontal orientation

Requirements
AppCam requires
¢ Mac OS X 10.5 Leopard (the script makes use of the newly introduced Scripting Bridge)
and was successfully tested on Intel & PowerPC based Macs

Installation & Usage
Just download and extract the ZIP archive and then open the AppCam script with a double click. After a short time you will see the created AppCam files in the following folder on your Mac:

~/Library/Application Support/AppCam/current/

Note
AppCam is not a ready-to-use AppleScript, but rather a proof of concept that needs your personal ideas. You will have to add your own custom functions to the code to process the generated AppCam files. I added an example function that simply opens all generated AppCam files to get you started. Just imagine to upload the AppCam images with curl to your FTP server and use them in the sidebar of your WordPress blog! Or as your signature image! A thousand possibilities! Yes, AppCam puts YOU in the driver seat :smiley: But see the warning below:

Warning
Publishing AppCam images can result in copyright conflicts, as you (most probably) don’t own the copyright of the shown application icons.

Known Problems
AppCam currently relies on built-in internal properties and not on user-specific preferences that are imported at startup. Therefor please keep in mind that changes made to the internal properties below will affect all users using this AppleScript.

Settings
If you open the AppCam script with the Script Editor, you will see a number of properties that can be easily edited to adjust AppCam to your very own requirements.

You can:
¢ alter the update frequency (by default AppCam creates a new image set every 5 minutes)
¢ choose between PNG or JPG output (JPEG does not offer transparency!)
¢ set the image orientation (horizontal, vertical or both [= 2 image files])
¢ define the height in pixel all application icons are scaled to
¢ choose to create a text file containing the corresponding application names and the creation date/time of the current AppCam image (by default AppCam will create it)
¢ populate a list with application/process names that will be ignored by AppCam

Note: After you edited the properties and saved the AppCam script, please make sure that the Python script named «combimgs.py» is still located in the Application bundle:
AppCam.app/Contents/Resources/combimgs.py

Image Creation
The creation of the AppCam images is done by a Python script (located inside the Script bundle) which uses the newly introduced Scripting Bridge and the NSImage class.
This way AppCam does not need to rely on external libraries or tools like PIL, ImageMagick or iMagine Photo. Just built-in Mac OS X technologies are used.

How It Works
Once started, AppCam will stay open and execute the following process in predefined time intervals:

  1. Create a list of the currently running applications using System Events
  2. Process this list to get the file paths of the application icons
  3. Convert the found application icons to TIFF format and scale them to the predefined size using Image Events
  4. Create one or more (Dock-like) AppCam images containing the scaled TIFF versions of the application icons using a Python script and Scripting Bridge (NSImage)
    (5.) Create a text file containing the creation date/time of the current AppCam images and the corresponding application names

All AppCam files are saved in its Application Support folder:
~/Library/Application Support/AppCam/current/

Special thanks to Joe Hewitt for the NSImage sample code!

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


-- created: 07.05.2008
-- modified: -/-
-- current version: 0.1
-- history:
-- ¢ v0.1:
--   + first public release
-- requires:
-- ¢ Mac OS X 10.5 Leopard
-- (the script makes use of the newly introduced Scripting Bridge)
-- ¢ a fast Apple Macintosh computer is recommended
--
-- special thanks to:
-- Joe Hewitt for his NSImage sample code which I found here:
-- <http://www.joehewitt.com/blog/workflows_part_2.php>

-- |-|-|-|-|-|-|-|-|-|-|-|-|-|-|
-- Description 
-- |-|-|-|-|-|-|-|-|-|-|-|-|-|-|

-- Welcome to the amazing AppCam!
--
-- This AppleScript works like a webcam for your currently running applications.
-- Once started, AppCam will stay open and execute the following process in
-- predefined time intervals:
--
-- 1. Create a list of the currently running applications
-- 2. Process this list to get the file paths of the application icons
-- 3. Convert the found application icons to TIFF format and scale them to the predefined size
-- 4. Create one or more (Dock-like) AppCam images containing the scaled TIFF versions of the application icons
-- (5.) Create a text file containing the creation date/time of the current AppCam images and
-- the corresponding application names
-- 6. Now it's your turn: Write your own custom functions to further process the created AppCam files!
--
-- All AppCam files are saved in the Application Support folder:
-- ~/Library/Application Support/AppCam/current/
--
-- USAGE:
-- Just open AppCam with a double click on its icon. After a short time you will see the created
-- AppCam files in the above mentioned Application Support folder.
--
-- NOTE:
-- AppCam is not a ready-to-use AppleScript, but rather a proof of concept. You will have to add your
-- own custom functions to process the generated AppCam files (upload to an FTP server, etc.).
-- I added an example function that simply opens all generated AppCam files to get you started.
--
-- WARNING:
-- Publishing AppCam images can result in copyright conflicts, as you (most probably)
-- don't own the copyright of the shown application icons.
--
-- KNOWN PROBLEMS:
-- AppCam currently relies on built-in internal properties and not on
-- user-specific preferences that are imported at startup. Therefor changes made to the
-- internal properties below will affect all users using this AppleScript.
--
-- IMAGE CREATION:
-- The creation of the AppCam images is done by a Python script (located inside the
-- Script bundle) which uses the newly introduced Scripting Bridge and the NSImage class.
-- This way AppCam does not need to rely on external libraries or tools like PIL, ImageMagick
-- or iMagine Photo.
-- Just built-in Mac OS X technologies are used.
-- The Python script can be found here:
-- AppCam.app/Contents/Resources/combimgs.py

-- |-|-|-|-|-|-|-|-|-|-|-|-|-|-|
-- Core Properties 
-- |-|-|-|-|-|-|-|-|-|-|-|-|-|-|

-- the name of this AppleScript
property mytitle : "AppCam"

-- |-|-|-|-|-|-|-|-|-|-|-|-|-|-|
-- The following properties can be edited to adjust
-- the script to your very own requirements.
-- |-|-|-|-|-|-|-|-|-|-|-|-|-|-|

-- this property defines the time interval in seconds in which
-- AppCam will create one or more AppCam images containing
-- the icons of the currently running applications
-- default value: 300 (= 5 minutes)
property updfrequency : 300

-- this property defines the image format used for the AppCam image
-- default value: "PNG" (= PNG format)
-- alternative value: "JPG" (= JPEG format)
-- >> JPEG does not offer transparency! PNG is recommended!
property appcamimgformat : "PNG"

-- this property defines the orientation of the AppCam image, horizontal or vertical
-- default value: "hv"
-- alternative values:
-- "h" >> one AppCam image with horizontal orientation is produced
-- "v" >> one AppCam image with vertical orientation is produced
-- "hv" >> two AppCam images are produced (vertical & horizontal orientation)
property appcamimgorient : "hv"

-- this property defines the height in pixel of the AppCam image
-- all application icons are scaled to match this height
-- default value: 32
-- alternative values: well, try 64 or 128 ;) think BIG!
property appcamimgsize : 32

-- this property defines if you also want to create a text file
-- containing the corresponding application names and the
-- creation date/time of the AppCam image
-- the text file features UTF-8 text encoding (with BOM)
-- >> this text file can be helpful when integrating AppCam into your
-- blog or website
-- default value: true
-- alternative value: false
property appcaminfo : true

-- this property defines certain application/process names that will
-- be ignored by AppCam. maybe you are a Firefox developer and don't
-- want to show the world that you are actually surfing the web with
-- Internet Explorer?
-- default value: {} (empty list)
-- alternative values: {"Safari", "iCal"}
property ignoreappnames : {}

-- |-|-|-|-|-|-|-|-|-|-|-|-|-|-|
-- Main Script
-- |-|-|-|-|-|-|-|-|-|-|-|-|-|-|

-- I am the idle handler (AppCam is a stay-open script),
-- I am controlling the application flow
on idle
	try
		-- removing the temp folder containing the temporary
		-- image files (maybe the script did not quit properly the last time?)
		my remtmpfolder()
		-- getting the names and icon paths of the currently running applications
		set appicondict to my getappicondict()
		-- creating mission critical folder paths (if they do not yet exist)
		my crtappfolders()
		-- creating scaled TIFF images of the retrieved icon files and getting
		-- the image file paths in return
		set imagefilepaths to my crtimagefiles(appicondict)
		-- creating the new AppCam image(s) containing all the previously scaled
		-- TIFF images and getting the AppCam image file path(s) in return
		set appcamimgpaths to my crtappcamimages(imagefilepaths)
		-- creating a text file containing the creation date/time of the current
		-- AppCam image and the names of the applications shown in the AppCam image
		if appcaminfo is true then
			set appcaminfopath to my crtappcaminfofile(appicondict)
			set appcamfilepaths to appcamimgpaths & {appcaminfopath}
		else
			set appcamfilepaths to appcamimgpaths
		end if
		-- ### Put your code here ###
		-- Maybe you want to use the AppCam image
		-- as an eMail signature in MS Entourage? Or in iChat? Be creative :D
		-- The following function is an example to get you started.
		-- ### Put your code here ###
		my openfiles(appcamfilepaths)
	on error errmsg number errnum
		my dsperrmsg(errmsg, errnum)
	end try
	return updfrequency
end idle

-- I am returning the Mac path of AppCam's application support folder
on getmyappsuppfolderpath()
	set appsuppfolderpath to ((path to application support folder from user domain) as Unicode text)
	set myappsuppfolderpath to (appsuppfolderpath & mytitle & ":")
	return myappsuppfolderpath
end getmyappsuppfolderpath

-- I am creating mission-critical subfolders in AppCam's application support folder 
-- 1) /Application Support/AppCam/current/
-- 2) /Application Support/AppCam/tmp/
on crtappfolders()
	set myappsuppfolderpath to my getmyappsuppfolderpath()
	set curfolderpath to myappsuppfolderpath & "current:"
	set tmpfolderpath to myappsuppfolderpath & "tmp:"
	repeat with folderpath in {curfolderpath, tmpfolderpath}
		set command to "mkdir -p " & quoted form of (POSIX path of folderpath)
		set command to command as «class utf8»
		do shell script command
	end repeat
end crtappfolders

-- I am returning the names and icon paths of the currently running applications
-- >> format of the returned list: {{application name, icon file path}, {etc.}, {etc.}}
on getappicondict()
	-- getting the names and file paths of/to the currently running applications
	tell application "System Events"
		set appprocesses to every application process
		set appbundles to {}
		set appnames to {}
		repeat with appprocess in appprocesses
			set appbundles to appbundles & application file of appprocess
			set appnames to appnames & name of appprocess
		end repeat
	end tell
	-- creating the list of application names and corresponding icon file paths
	set countappbundles to length of appbundles
	set appicondict to {}
	repeat with i from 1 to countappbundles
		try
			set appbundle to item i of appbundles
			set appname to item i of appnames
			set appbundlepath to POSIX path of (appbundle as Unicode text)
			-- ignoring helper applications, system processes and applications/processes listed
			-- in the «ignoreappnames» property
			if appbundlepath contains "/Applications/" and appbundlepath does not contain "/Contents/" and appname is not in ignoreappnames then
				-- the file name of an application icon can be retrieved from the «Info.plist» file located
				-- inside of an application bundle
				set infoplistpath to appbundlepath & "Contents/Info"
				-- the key of interest is «CFBundleIconFile»
				set command to "defaults read " & quoted form of infoplistpath & " CFBundleIconFile"
				set command to command as «class utf8»
				set appicnsfilename to do shell script command
				-- sometimes the icon name does not have the proper '*.icns' suffix
				if appicnsfilename does not end with ".icns" then
					set appicnsfilename to appicnsfilename & ".icns"
				end if
				-- application icons are always saved in 'Appname.app/Contents/Resources/', so
				-- now having the icon name and application path we can easily create the full path
				set appicnsfilepath to (appbundlepath & "Contents/Resources/" & appicnsfilename)
				set appicnsfilealias to (POSIX file appicnsfilepath) as alias
				set appicondict to appicondict & {{appname, appicnsfilepath}}
			end if
		on error errmsg number errnum
			-- for debugging only...
			-- log errmsg
		end try
	end repeat
	return appicondict
end getappicondict

-- I am creating scaled TIFF images of the given application icons using «Image Events»
-- and return a list containing file paths to the created images
on crtimagefiles(appicondict)
	set imagefilepaths to {}
	repeat with appiconentry in appicondict
		set appname to item 1 of appiconentry
		set appiconpath to item 2 of appiconentry
		set tiffimgpath to (my getmyappsuppfolderpath() & "tmp:" & appname & ".tiff")
		tell application "Image Events"
			launch
			set openimage to open appiconpath
			scale openimage to size appcamimgsize
			save openimage as TIFF in tiffimgpath with icon
			close openimage
		end tell
		set imagefilepaths to imagefilepaths & tiffimgpath
	end repeat
	return imagefilepaths
end crtimagefiles

-- I am creating the new AppCam images from the given image files and return a list
-- containing file paths to the created AppCam images
on crtappcamimages(imagefilepaths)
	-- as we are going to talk to a Python script next, we need to convert the
	-- image file paths to a large string of quoted Posix paths
	set strpaths to ""
	repeat with imagefilepath in imagefilepaths
		set strpaths to strpaths & quoted form of (POSIX path of imagefilepath) & space
	end repeat
	
	set appcamimagepaths to {}
	-- creating the appropriate AppCam images
	repeat with orient in appcamimgorient
		-- creating AppCam image file name
		if appcamimgformat is "PNG" then
			set appcamimgname to "appcam" & orient & ".png"
		else if appcamimgformat is "JPG" then
			set appcamimgname to "appcam" & orient & ".jpg"
		end if
		-- creating AppCam image file path
		set appcamimgpath to (my getmyappsuppfolderpath() & "current:" & appcamimgname)
		-- calling the Python script responsible for the image creation
		set pyscriptpath to ((path to me) as Unicode text) & "Contents:Resources:combimgs.py"
		set command to "python " & quoted form of (POSIX path of pyscriptpath) & space & orient & space & appcamimgformat & space & quoted form of (POSIX path of appcamimgpath) & space & strpaths
		set command to command as «class utf8»
		do shell script command
		
		set appcamimagepaths to appcamimagepaths & appcamimgpath
	end repeat
	
	return appcamimagepaths
end crtappcamimages

-- I am creating an AppCam info text file (UTF-8 + BOM) containing the creation
-- date/time of the current AppCam images and the corresponding application names
-- I return the file path of the info text file
on crtappcaminfofile(appicondict)
	set filecont to ""
	-- current date in format: 2008-05-08, 23:02:04 (GMT+0200)
	set curdate to do shell script "date \"+%Y-%m-%d, %H:%M:%S (GMT%z)\""
	set filecont to filecont & curdate
	-- adding the current application names
	repeat with appiconentry in appicondict
		set appname to item 1 of appiconentry
		set filecont to filecont & return & appname
	end repeat
	-- creating the info text file
	set appcaminfopath to (my getmyappsuppfolderpath() & "current:appcam.info")
	try
		set openfile to open for access appcaminfopath with write permission
		set eof of openfile to 0
		set BOM_UTF8 to ((ASCII character 239) & (ASCII character 187) & (ASCII character 191))
		write BOM_UTF8 to openfile
		write filecont to openfile as «class utf8» starting at eof
		close access openfile
	on error errmsg number errnum
		try
			close access openfile
		end try
		error errmsg number errnum
	end try
	return appcaminfopath
end crtappcaminfofile

-- I am removing the folder containing the temporary icon images
on remtmpfolder()
	try
		set tmpfolderpath to my getmyappsuppfolderpath() & "tmp:"
		set command to "rm -r " & quoted form of (POSIX path of tmpfolderpath)
		set command to command as «class utf8»
		do shell script command
	end try
end remtmpfolder

-- I am simply trying to open the given file paths
on openfiles(filepaths)
	repeat with filePath in filepaths
		set command to "open " & quoted form of (POSIX path of filePath)
		set command to command as «class utf8»
		do shell script command
	end repeat
end openfiles

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