Using Keynote as a Digital Sign / Kiosk

Edit: Snow Leopard Problems 11/09/2009 I am seeing some issues with Snow Leopard and this script. It seems that Snow Leopard has a different definition of what constitutes user interaction in as much as the screen saver is interrupted by Keynote now. The practical upshot (downshot?) is that when the current presentation ends the screen saver is not running and the desktop is visible. If you have multiple monitors, you can simply tell keynote to output to the alternate display and set that monitor’s desktop background to solid black (or whatever). However if you have a single monitor, I don’t yet have any reasonable work around. For the time being, do not upgrade any machine running Kiosk to Snow Leopard. I am working on finding a solution, but unfortunately I am not confident that I will be able to do so.

Edit: Updated 02/05/2009 Confirmed that the script works with Keynote 09, though if you have both 09 and 08 there can be issues (so don’t do that). Also, make sure that all your presentations, including your “screen saver.key” are updated to Keynote 09 format (just open and save them). Some minor tweaks to the code, including a lot of cleanup and additional comments. If you end up using this script in a production environment, I’d appreciate an email tell me how you’re using it!

Recently my group was tasked with creating a Digital Sign system. We evaluated several commercial products but ultimately settled on using a Mac mini and Keynote. We chose the Sharp LC-52D62U as a display because it is a full native 1920x1080p LCD display with HDMI. The inspiration to use Keynote for this project is this site.

Anyway, I wrote the following Apple Script applet to drive the display. You’re free to use the below code as long as you include an attribution to myself. We’ve run this on a G5 and on an Intel Mac Mini but YMMV.

Some important information before the code:

  1. The script requires the following folders in the same location as itself. You can rename them if you like.
    -Active Presentations ← The keynote files you wish to display go here. They are processed in (Apple) Alphabetical order.
    -Kill ← Place any file in this folder to make the script terminate when the current presentation ends. Check this folder for files if you are trouble-shooting.
    -updates ← Any files placed here will be moved (with replacing) to the Active Presentations folder when the current presentation ends.

  2. Make sure that your keynote files follow these guidelines:
    -All slides and embedded objects MUST automatically transition or it won’t go anywhere. (HOWEVER, do NOT set the presentation to auto-start on open as the script will start it for you. Should you be so inclined you can edit the script to change this.)
    -Save your presentations with the First Slide selected. (For some reason Keynote starts the presentation from the selected slide when using Applescript.)
    -You will need a place holder presentation called “Screen Saver.key” which will be displayed while updated files are copied to the Active Presentations folder.
    -If you use any web content, make several smaller presentations instead of one big one. Then set the Web objects in each presentation to update automatically. (When the presentation switches to the next one, these objects will get updated. We use this to have semi-dynamic content on our sign.) Edit: [b]Apparently web views have been dropped from Keynote? Not sure when this happened…[b]
    -It is strongly recommended to always embed all video and other files rather than linking to them.

  3. Set the copy of Keynote on the Digital Display computer with these settings:
    -Set Keynote to use a Template for new documents instead of the Template Chooser.
    -If you have multiple monitors make sure that keynote is set to display on the correct monitor.
    -You may have to enable the “Allow Expose, Dashboard, and others to use the screen” option. (The jury is still out on whether this is really needed or not.) Edit: Seems that Keynote 09 has some issues with this setting. We’ve turned it off on our sign with no ill effect.

  4. Settings for the Digial Display computer:
    -Enable System Preferences → Energy Saver → Options → “Restart automatically after a power failure”
    -Enable a Screen saver. This will obscure the Desktop when changing between presentations. (I suggest using “Basic Black.saver”.)
    -Set an automatic login and set the Applet to auto-launch.
    -Enable File Sharing or “Remote Login” to allow you to remotely upload files to the updates folder via AFP or SFTP.

If you have any comments or questions, let me know!


--Kiosk v1.3
--
--Written by Steven Hunter (hunters@NOSPAMpurdue.edu)
--
--Version History:
--1.0 Initial Release
--1.1 Added automatic file updating mechanism, set single ß
--1.2 Added improved error reporting, fixed paths to be relative for better portability, added checks to make sure that update files aren't still being copied.
--1.2a Corrected spelling error and tweaked error reporting for Display_This()
--1.3 Fixed some niggling bugs and cleaned up the code a bit.

global base_path --The path to this program
global presentation_path --The path to the location of the currently active .key files
global update_file --The path to folder containing new or updated .key files
global kill_file --The path to the "killfile" folder. If any items are found in this folder when the current presentation ends, everything shuts down.

tell application "Finder" --Setting the value of these globals now.
	set base_path to container of (path to me) as string
	set presentation_path to base_path & "Active Presentations"
	set update_file to base_path & "updates"
	set kill_file to base_path & "Kill"
end tell

global last_error_time -- Used to prevent the system from sending excessive error messages.
global error_rate -- The number of seconds between error messages. Default is 5 seconds.
global system_name -- Inserted into the subject line of error email messages, this should be a Unique identifier for your system; the default is "Digital Sign".
global error_email
(*The email address to which errors should be sent. The email will come from "username@hostname" where "username"
is the short name of the user running Kiosk and "hostname" is the fully qualified Unix hostname of the Mac running Kiosk.*)

set last_error_time to current date
set error_rate to 5
set system_name to "Digital Sign"
set error_email to "Your Email Goes Here!!"

set File_List to my Build_File_List(presentation_path)
set presentation_count to count of items in File_List

if presentation_count is less than 1 then
	my report_error("[" & system_name & "] Error: No presentation files found!", current date)
	display dialog "No presentation files found, or error enumerating presentation list! Please contact your IT support group." buttons {"OK"}
	tell me to quit
end if

set x to 1

tell me to activate

display dialog "Now beginning show, you have 5 seconds to Cancel." buttons {"Continue", "Cancel"} default button "Continue" giving up after 5

do shell script ("open /System/Library/Frameworks/ScreenSaver.framework/Resources/ScreenSaverEngine.app")
--I recommend the "Basic Black" screen saver. See http://www.monkeybreadsoftware.de/Freeware/BasicSaver.shtml

repeat --This is the main loop where all the magic happens.
	
	if x is greater than presentation_count then set x to 1 --If we've gone through all the presentations then it's time to start over.
	
	set updatekill to my update_kill()
	
	if updatekill is "kill" then
		tell me to activate
		my terminate_keynote(false)
		display dialog "Killfile detected: Exiting Keynote" buttons {"OK"} default button "OK" giving up after 5
		exit repeat
	else if updatekill is "update" then
		my Update_Slides()
		set File_List to my Build_File_List(presentation_path)
		set presentation_count to count of items in File_List
		set x to 1
	else
		my Display_this(item x in File_List)
		set x to x + 1
	end if
	
end repeat

on Build_File_List(aPath)
	set theFile_list to null
	tell application "Finder"
		set temp to aPath as alias
		select (every file in temp)
		set theFile_list to the name of every file in temp
		close every window
	end tell
	return theFile_list
end Build_File_List

on Display_this(aFile)
	try
		tell application "Keynote"
			my close_current_document()
			open (presentation_path & ":" & aFile)
			start slideshow
			set foo to true
			repeat until foo is false
				repeat with curDoc in slideshows
					if (playing of curDoc) is true then
						delay 1
					else
						set foo to false
						delay 1
						my close_current_document()
					end if
				end repeat
			end repeat
		end tell
		
	on error number n
		my report_error("[" & system_name & "] Error (" & n & ") : Display_this, file: " & aFile as string, current date)
		
	end try
end Display_this

on Update_Slides()
	try
		repeat
			tell application "Keynote"
				my close_current_document()
				open base_path & "Screen Saver.key"
				start slideshow
				
				set foo to true
				repeat until foo is false
					repeat with curDoc in slideshows
						if (playing of curDoc) is true then
							my do_update()
							delay 1
						else
							set foo to false
						end if
					end repeat
				end repeat
			end tell
			
			set updatekill to my update_kill()
			if updatekill is "kill" then exit repeat
			if updatekill is "empty" then
				my close_current_document()
				my terminate_keynote(true)
				exit repeat
			end if
		end repeat
		
	on error
		my report_error("[" & system_name & "] Error: Update_Slides", current date)
		
	end try
end Update_Slides

on close_current_document()
	tell application "Keynote"
		repeat with curDoc in slideshows
			try
				close curDoc
			end try
		end repeat
	end tell
end close_current_document

on do_update()
	try
		tell application "Finder"
			set source to update_file as alias
			set destination to presentation_path as alias
			
			set updated_files to every item of (source as alias)
			repeat with i from 1 to number of items in updated_files
				set this_item to item i of updated_files
				set last_size to -1
				set copy_done to false
				
				repeat while copy_done is false --This loop makes sure that the file has finished being copied to the updates folder.
					tell application "System Events"
						set curr_size to size of this_item
					end tell
					
					if curr_size > last_size then
						set last_size to curr_size
						delay 1
					else
						tell application "System Events"
							set curr_size2 to size of this_item
						end tell
						if (curr_size2 is not equal to curr_size) and (last_size is not equal to curr_size) then
							delay 1
						else
							set copy_done to true
						end if
					end if
				end repeat
			end repeat
			
			move (every file in source) to destination with replacing --Since the script doesn't clean up the "active" folder, if you want to remove depreciated presentations, you'll have to do it manually.
		end tell
		
	on error
		my report_error("[" & system_name & "] Error: do_update", current date)
		
	end try
end do_update

on update_kill() --Looks for updated presentation(s) in the Updates folder and for a kill file in the Kill folder. The Kill file always overrides updates.
	try
		tell application "Finder"
			set update_yn to count of files in folder (update_file as alias) --Keep the update folder clear of non-keynote files as this script doesn't differentiate between them!
			set kill_yn to count of files in folder (kill_file as alias)
		end tell
		if kill_yn is greater than 0 then return "kill"
		if update_yn is greater than 0 then return "update"
		return "empty"
	on error
		my report_error("[" & system_name & "] Error: update_kill", current date)
		
	end try
end update_kill

on report_error(error_message, error_time)
	set difference to (error_time - last_error_time)
	if difference is greater than error_rate then --Has there been at least (error_rate) seconds since the last error email was sent? This thing can mail a LOT of errors when something screws up.
		do shell script ("mail -s " & quoted form of error_message & " " & quoted form of error_email & " < /dev/null > /dev/null")
		set last_error_time to error_time
	end if
end report_error

on terminate_keynote(restart_yn) --This was added so I could kick, or just kill, Keynote should the need arise.
	tell application "System Events"
		if process named "Keynote" exists then
			tell application "Keynote" to quit saving no --Try to quite Keynote
			delay 5 --Give it a few seconds to exit
			if process named "Keynote" exists then --But if it is still running...
				set proc_id to (unix id of processes whose name is "Keynote") --get the PID so
				do shell script ("kill -9 " & proc_id) --we can forceably kill it. 
				delay 5 --Give it a few seconds to be killed 
			end if
		end if
		
		if restart_yn is true then
			tell application "Keynote" to activate
			my close_current_document()
		end if
	end tell
end terminate_keynote

First off, thanks Steven for providing this code to others for use. Having never done much with applescript it is saving me a ton of research :slight_smile: I am building a digital sign with a Intel Mac Mini / Keynote iWork-06(all currently patched up to date) hooked into a 60 inch plasma display rotated 90 degrees (768x1366) I was fortunate to find this script because it describes a great deal of what I am trying to do. I am having a couple of problems with it, even after following every direction to the letter.

The black screensaver starts as it should, and the slideshow loads but even if the script starts the slideshow, the screen stays black, until I hit a key or move the mouse. When I do this I often see keynote loaded in “edit mode” I guess where you work on the slide, sometimes the slideshow starts and sometimes Keynote crashes. Shouldn’t the slideshow take over the screen and close the screensaver? Is there a way to make sure this happens? Also, is there someway to launch a keynote slideshow from your script without having the option of editing it, ie just play it and close, load the next slide, repeat? I dont want the edit window to ever come up, I just want the slides to play…

I imagine you or someone else here has run into this in some way. If you know of a solution, please post it :slight_smile: I am going to research it some more.

thanks!

Joe

You’re quite welcome. :slight_smile:

The screen saver should run continuously, but keynote should take over the screen when it needs to. Uncheck the “Allow Expose, Dashboard, and others to use the screen” option in Keynote. Try disabling the screensaver by commenting out this line:

do shell script ("open /System/Library/Frameworks/ScreenSaver.framework/Resources/ScreenSaverEngine.app")

And see if that changes anything. I had another person email me to say that he had issues until he commented out that line. Oh, and you might also comment out the “try/end try” and “on error” blocks so you can see if it is generating an error you can fix.

Not that I could find. Usually when you tell keynote to open a file, it always opens it in Edit mode, starting on the slide you were working on when it was last saved. (This is why it is important to always go to the first slide in the presentation before you save it.) This is also why you run the screen saver, to obscure this behavior. BTW, definitely do NOT set your presentation to “automatically play upon open” without modifying the script. This could cause undesired behavior.

Is your keynote patched up to v3.02?

Steven,

Thanks for the quick and helpful reply. Disabling the screen saver seems to allow the presentations to run correctly, but unfortunately now between slide shows viewers see the desktop and the next slide show loading, which for a digital sign, I am trying to avoid. I’m going to see what, if anything, I can do either via Keynote or applescript that would make this happen. If you have any suggestions I’ll happily put them to the test :slight_smile: Great script :slight_smile: Now if I can just “hide” the backend, I’ll be giddy as a schoolgirl!

Cheers,
Joe Jenkins

See if any of the Apple provided screensavers will work instead. Worst case you can use the picture slide show one with a couple of plain black JPEGs. :slight_smile:

Also, is the account that is running the script an Admin or Regular account?

Steven,

I tried one of the Apple screen savers and its working out much better with that. I am going to test out just making a black image photo the same rez as the display and set the screen saver to run that one. Thanks for the suggestion :slight_smile: I’m not running as admin, just regular user, and its Keynote 3.02 :slight_smile:

Thanks,
Joe

Working much better now :slight_smile: I made a black image jpg the same resolution as the display and just put it into its own folder, and then I set the Apple screen saver to “Choose Folder” and pointed it at that folder. That works really well! The only “minor” annoyance now, is that while loading between presentations I see the mouse pointer flicker on the screen a bit, even if I stuff it in a corner. Is there any way you know of off the top of your head to hide the pointer off the screen? I’ll do some searching in the forums too. Great script! I’ll post a picture or two of the screen shortly so you can see what your handywork is doing out here on my end :slight_smile:

Joe

Steven,

Got it pretty much working to my satisfaction, using the scripting addition below. Basically its just a “hide mouse” in the script in the before the slide show runs, and it takes care of hiding the pointer. Thanks for all your help, I’ll post a pic or two here soon!

XTool for Mac OS X : http://lestang.org/osax/XTool-2.0.dmg.tgz
XTool sources : http://lestang.org/osax/XTool-2.0-src.dmg.tgz

Here is a shot of our completed 60" screen up and running and looking great. Thanks so much for the help, I just thought you’d like to see what we’re doing with your code :slight_smile:

Cheers,
Joe Jenkins
Network Engineer
Davis Tool, Inc

http://www.nerdnet.com/?q=node/78

Hardware: LG 60" HD Plasma - DVI connected to an Intel Mac Mini dual core 1.6 Ghz running Mac OS X 10.4.9 / 512MB Ram

Sorry I didn’t reply earlier but the forum didn’t alert me that this thread got updated.

Looks good, I’m glad you found it useful! If possible, please add somewhere in your blog post that I work for the Department of Biological Sciences at Purdue University in West Lafayette, IN (USA).

:smiley:

(Just bumping this due to update)

Great script!
Is it possible to load the folder with presentations and trigger these at certain times?

i.e. If Time = 11:00am run 11.key, If Time = 12pm run 12.key

I can only pull off the basics in AS, and this seems to be way beyond me.

Surprisingly, this is ridiculously difficult to do. I had originally intended to do exactly that, have certain presentations load at different times.

What I found was that Keynote really does NOT like to be interrupted by Applescript. If you manually start and stop the presentations, it will do it all day long. As soon as you have Applescript tell it to start and stop, it begins to freeze, return superfluous errors, and generally misbehave. I have no idea why this would be the case.

Now granted the last time I worked on that function was with Keynote 08, and now that 09 is out I may revisit it when I get some spare time. (So keep your fingers crossed. :smiley: )

If you don’t mind me asking, are you using the script in a production environment?

No not yet.
I have some digital signage to setup next week for an event called Jazz Weekend at Central Michigan University, and I’m looking for some ways to update the list of who’s currently playing at venues and who’s up next.

Jon

This would be a quick-n-dirty way to do it: Get a blog somewhere (or setup your own WordPress site) and put the venue information into it. Then point the Apple provided RSS screen saver towards that site’s RSS feed and remove or disable the mouse and keyboard.

Hi Steven,

Just wanted to let you know we still use your excellent script - been almost two years now - I just updated it today. I’ve had one mac mini die and a lot of keynote crashing but your script just plugs along fine. Thanks again! Do you know if it will work with the newest version of Keynote '09? I think we’re ready to update the software on our display’s Mac Mini. Thanks!

Joe Jenkins
Davis Tool Inc

Yes, the version 1.3 does indeed work with Keynote '09. (Actually older versions worked too, but I used the '09 release as an excuse to clean up my code a bit so you should use it instead anyway.)

Glad to hear that you are still using the script! :slight_smile:

Thanks!!

Just an update - your updated script works great, seems to run faster and the Mac Mini has been happier over all :slight_smile: The only problem now seems to be Keynote specific - the infamous “Web Object” - which as you know has been removed or ‘hidden’ at least in Keynote '09…

I can still get my slides with web objects to work - they no longer update when the slide is loaded. I’ve been testing a lot of different ways to force this to work, but so far, no go… I may try a meta tag refresh in my dynamic web code for each object but it might screw things up. Have you seen anything that might fix this? I’ve been digging through Applescript as well trying to see if via Applescript you can tell Keynote to load the slide or the content new or disregard the cache… No luck so far. Anyway, I’ve reported it to Apple, as I think a lot of other disappointed users have. The digital sign is much more fun when I have it pulling live dynamic content off of rss feeds, weather, etc pages I have coded…

If I can come up with a solution, I’ll post my findings here… Hopefully Apple will listen to their users :slight_smile:

Cheers and thanks again :slight_smile:
Joe Jenkins
Davis Tool Inc

just a postscript: I’ve actually started building a solution in Adobe Flex / Air that may ultimately replace my Keynote system - I would rather just use Keynote but if I have to I’ll code my own custom solution - the AIR player is small and it runs on Mac, Linux, and Win and I can use the code to fetch dynamic web pages and objects and display them - I just need to figure out a good way to store “slides” or sign data and then go through a sequence of prebuilt slides… I haven’t had much time to work on it but if it gets interesting I’ll put it online…