It is now more or less six years since I began watching telly exclusively using eyeTV running on my mac. To this day I have not found a better PVR, and I have tried a few. It has its niggles and features, but I am largely satisfied with its functionality. With ever larger screens available, it allows me to browse and watch telly at the same time. In fact it allows me to do whatever I am doing whilst watching telly in a small window. Sometimes I just use it the way I would a telly, with remote control and all. My current set up runs on a 2011 mac mini, using two Elgato DTT tuners. This allows me to record two programs simultaneously, or watch one and record on the other. I tend to record everything I want to watch. This way, I can pretty much avoid advertising, by never watching real-time (although limits the possibility of joining the twitter debate).
Prior to eyeTV 3, the downside was always having to leave the machine running, so as not to miss recording. The advent of eyeTV brought triggered scripts, whereby a script could be initiated on completion of a recording. Various RecordingDone scripts became available for download. I duly installed one of these, which would trigger after any program was recorded and if no other recording was in progress, it would sleep or close down the machine. It was now possible to save a bit of electricity usage, and remain confident that I wouldn’t miss any recordings.
The following is a common example:
on RecordingDone(recordingID)
set isCurrentlyRecording to check_eyetv_is_recording()
if isCurrentlyRecording is false then
with timeout of 300 seconds
tell me to activate
display dialog "Warning: EyeTV has now finished recording and the system will automatically shut down in 5 minutes unless you click Stop!" with icon 2 buttons {"Stop!"} giving up after 300 default button "Stop!"
set theresults to the result
if gave up of theresults is true then
tell application "System Events" to sleep
end if
end timeout
else
quit
end if
end RecordingDone
to check_eyetv_is_recording()
delay 30
tell application "EyeTV"
if is_recording then
return true
else
return false
end if
end tell
end check_eyetv_is_recording
After a short while, I noticed a number of issues with this script. Often if I was browsing, whilst a recording came to an end, the dialog box may end up in the back ground and un noticed, so that the machine would just shut down whilst I continued to browse. Sometimes if two programs were recording, and ended simultaneously, one instance of the script would be un noticed so the machine would shut down. The only solution was to just get on and learn a bit of AppleScript and fix all the problems. So here is my solution, and also my first AppleScript. Try it if you wish.
So, what have you done with a 400 line script that couldn’t be done in 26? I hear you all ask.
Essentially I wanted to achieve the following:
- Only allow one instance of the script to run at any one time
- Log everything the script was up to
- Avoid a shutdown failure if Firefox was taking too long to quit (which it often does if you use Xmarks)
- Prevent the script from changing (or offering to change) the machine state if I was busy doing other things, such as
- Browsing
- Watchning EyeTV in full screen mode
- Watching Quicktime movie
- Listening to itunes
- Watching a DVD
So, you may think it is cumbersome, but it works for me. The code may not be the best AppleScript you ever saw, and there may be better ways of doing things, but It is my first go at AppleScript.
For those who have never used a RecordingDone script before, all you have to do, is copy the below code, and paste it into a new text file (use text editor if you like). Save the file with the name RecordingDone.scpt in nameofyourharddrive/Library/Application Support/EyeTV/Scripts/TriggeredScripts. (Create this folder if it doesn’t already exist).
Job done.
--originally written by Brendan Ratcliffe 2011 myredroom.wordpress.com
global LogMsg
global posixLogFileName, fullLogFileName, fullFileName, posixFileName
global recordingNow, idletime, otherInstance
global currentRecordingID, thisRecordingTitle, referenceNumber
global itunesRunning, quicktimeRunning, DVDrunning, firefoxrunning
global itunesPlaying, quicktimePlaying, DVDplaying
global timeNow, nowPlus15Mins, nowPlus60Mins, soonestrecordingStart, soonestrecordingtitle
on RecordingDone(recordingID)
set currentRecordingID to recordingID --save for later
my initLogFile() --set up log file
set errNum to 0
set itemno to 1
tell application "EyeTV"
repeat with recid in unique ID of every recording
if recid as text is equal to recordingID as text then
set mytitle to title of item itemno of every recording
my writetolog("RecordingDone script has been invoked, having completed recording (" & mytitle & ").")
end if
set itemno to itemno + 1
end repeat
end tell
set anotherInstance to my check_for_another_instance()
if anotherInstance then
return
end if
set idletime to (do shell script "ioreg -c IOHIDSystem | perl -ane 'if (/Idle/) {$idle=(pop @F)/1000000000; print $idle,\"\";last}'")
my writetolog("The system has been idle for: " & idletime & " seconds.")
try
set realidletime to idletime as real
on error
my writetolog("error occurred in line: \"" & "set realidletime to idletime as real" & "\"")
my deleteSwitchFile()
return
end try
try
set intidletime to round realidletime as integer
on error
my writetolog("error occurred in line: \"" & "set intidletime to round realidletime as integer" & "\"")
my deleteSwitchFile()
return
end try
tell application "EyeTV"
if is_recording = true then
my writetolog("eyeTV is currently recording another program. Therefore the machine state will not be altered.")
my deleteSwitchFile()
return
end if
--if playing is true and full screen is true then
if full screen is true then --altered to handle bug in 3.5.4
my writetolog("eyeTV is currently playing in full screen mode. Therefore the machine state will not be altered.")
my deleteSwitchFile()
return
end if
if playing is true and full screen is false then
my writetolog("eyeTV is currently playing in a window.")
if intidletime < 120 then
my writetolog("eyeTV is currently playing in a window, but there has been user activity in the last 2 minutes. Therefore the machine state will not be altered.")
my deleteSwitchFile()
return
end if
end if
if playing is false then
my writetolog("eyeTV is not currently playing at all.")
if intidletime < 120 then
my writetolog("eyeTV is not currently playing, but there has been user activity in the last 2 minutes. Therefore the machine state will not be altered.")
my deleteSwitchFile()
return
end if
end if
set timeNow to current date
set nowPlus15Mins to (current date) + 15 * minutes
set nowPlus60Mins to (current date) + 60 * minutes
set soonestrecordingStart to (current date) + 1 * days
my writetolog("Now searching for future recordings between now and " & soonestrecordingStart & ".")
set itemno to 1
repeat with thisRecordingStart in start time of every program
if thisRecordingStart > timeNow and thisRecordingStart < soonestrecordingStart then
set soonestrecordingStart to thisRecordingStart
set soonestrecordingtitle to title of item itemno of every program
end if
set itemno to itemno + 1
end repeat
if soonestrecordingStart ≥ nowPlus15Mins then
my writetolog("The soonest future recording (" & soonestrecordingtitle & ") will begin at " & soonestrecordingStart & ".")
else
my writetolog("eyeTV is scheduled to start another recording within the next 15 minutes (" & soonestrecordingtitle & ").")
my writetolog("Therefore the machine state will not be altered.")
my deleteSwitchFile()
return
end if
end tell
--at this point there may be good reason to offer sleep,
--so lets do some more tests to determine what is going on on this system
--if the eyeTV is in full screen, but paused then lets offer sleep/shutdown
--now we will work out whether the system has other media player running
tell application "System Events"
set itunesRunning to (name of processes) contains "iTunes"
set quicktimeRunning to (name of processes) contains "Quicktime Player"
set DVDrunning to (name of processes) contains "DVD Player"
set firefoxrunning to (name of processes) contains "Firefox"
end tell
if itunesRunning is true then
tell application "iTunes"
if player state is playing then
set itunesPlaying to true
my writetolog("iTunes has been found to be running and playing. Therefore the machine state will not be altered.")
my deleteSwitchFile()
return
else
set itunesPlaying to false
my writetolog("iTunes has been found to be running but not playing. ")
ignoring application responses
quit
end ignoring
end if
end tell
else
set itunesPlaying to false
my writetolog("iTunes has been found not to be running. ")
end if
if quicktimeRunning is true then
tell application "QuickTime Player"
if exists document 1 then
my writetolog("Quicktime has been found to be running.")
set quicktimePlaying to false
repeat with thisWindowActive in playing of every document
if thisWindowActive is true then
set quicktimePlaying to true
my writetolog("Quicktime has been found to be running and playing. Therefore the machine state will not be altered.")
my deleteSwitchFile()
return
end if
end repeat
else
set quicktimePlaying to false
my writetolog("Quicktime has been found to be running but not playing. ")
ignoring application responses
quit
end ignoring
end if
end tell
else
set quicktimePlaying to false
my writetolog("Quicktime has been found not to be running. ")
end if
if DVDrunning is true then
tell application "DVD Player"
if dvd state is playing then
set DVDplaying to true
my writetolog("DVD Player has been found to be running and playing. Therefore the machine state will not be altered.")
my deleteSwitchFile()
return
else
set DVDplaying to false
my writetolog("DVD Player has been found to be running but not playing. ")
ignoring application responses
quit
end ignoring
end if
end tell
else
set DVDplaying to false
my writetolog("DVD Player has been found not to be running. ")
end if
my writetolog("Completed checks for other active players.")
if soonestrecordingStart > nowPlus15Mins and soonestrecordingStart < nowPlus60Mins then
my writetolog("got into first idle check check")
(beep 2) activate
set thisResponse to display dialog "EyeTV has just finished recording. Another recording will begin within the next hour. Would you like the computer to go into sleep mode?" buttons {"Yes", "No"} default button "No" with icon note with title "EyeTV-Recorder" giving up after 30
if button returned of thisResponse = "" then
my deleteSwitchFile()
my writetolog("System sent to sleep because no response to sleep dialog.")
tell application "Finder"
ignoring application responses
sleep
end ignoring
return
end tell
end if
if button returned of thisResponse = "Yes" then
my deleteSwitchFile()
my writetolog("System sent to sleep because user responded \"Yes\" to sleep dialog.")
tell application "Finder"
ignoring application responses
sleep
end ignoring
return
end tell
end if
if button returned of thisResponse = "No" then
my deleteSwitchFile()
my writetolog("System state unaltered because user responded \"No\" to sleep dialog.")
return
end if
else if soonestrecordingStart > nowPlus60Mins then
--my writetolog("got into second idle check.")
beep 2
activate
set thisResponse to display dialog "EyeTV has just finished recording. There are no scheduled recordings in the next hour. Would you like the computer shut down?" buttons {"Yes", "Sleep", "No"} default button "No" with icon note with title "EyeTV-Recorder" giving up after 30
if button returned of thisResponse = "Yes" then
if firefoxrunning is true then
my quitFirefox()
end if
my deleteSwitchFile()
my writetolog("System shutdown because user responded \"Yes\" to shutdown dialog.")
tell application "Finder"
ignoring application responses
shut down
end ignoring
return
end tell
end if
if button returned of thisResponse = "" then
if firefoxrunning is true then
my quitFirefox()
end if
my deleteSwitchFile()
my writetolog("System shut down because no response to shutdown dialog.")
tell application "Finder"
ignoring application responses
shut down
end ignoring
return
end tell
end if
if button returned of thisResponse = "Sleep" then
my deleteSwitchFile()
my writetolog("System sent to sleep because user responded \"Sleep\" to shutdown dialog.")
tell application "Finder"
ignoring application responses
sleep
end ignoring
return
end tell
end if
if button returned of thisResponse = "No" then
my deleteSwitchFile()
my writetolog("System state unaltered because user responded \"No\" to shutdown dialog.")
return
end if
end if
my writetolog("Reached end of recordingDone script without altering the machine state. We shouldn't really have got here. Yikes.")
my deleteSwitchFile()
--display dialog "we are at the end of the recording done script"
end RecordingDone
---------------------------------------------------------------------------------------------------------------------------------------
on initLogFile()
set fullLogFileName to ((path to documents folder as text) & "eyeTV-recordingDoneLog.txt")
set posixLogFileName to POSIX path of fullLogFileName
--check for existence of log file, if not existing then create it.
tell application "Finder"
if not (exists file fullLogFileName) then
do shell script "touch " & quoted form of posixLogFileName
my writetolog("This is the log file for eyeTV's recodingDone triggered script.")
my writetolog("This file may be deleted at any time, recordingDone will create a new log as required.")
end if
end tell
end initLogFile
---------------------------------------------------------------------------------------------------------------------------------------
-- testing code: this will not be called when triggered from EyeTV, but only when the script is run as a stand-alone script
on run
tell application "EyeTV"
set rec to unique ID of item 1 of every recording
my RecordingDone(rec)
end tell
end run
---------------------------------------------------------------------------------------------------------------------------------------
on writetolog(logLine)
set logLine to (current date) & ": eyeTV recordingID: " & currentRecordingID & ": " & logLine
set fullCommand to "echo \"" & logLine & "\" >> " & posixLogFileName
do shell script fullCommand
end writetolog
---------------------------------------------------------------------------------------------------------------------------------------
on quitFirefox()
writetolog("Firefox has been found to be running. An attempt will be made to quit it before shutting down.")
tell application "Firefox"
quit
delay 2 * minutes
end tell
set firefoxrunning to false
tell application "System Events"
set firefoxrunning to (name of processes) contains "Firefox"
end tell
if firefoxrunning is true then
my writetolog("After waiting 2 minutes, firefox is still running, so this script will wait a further 2 minutes.")
tell application "Firefox"
ignoring application responses
quit
end ignoring
end tell
delay 2 * minutes
else
my writetolog("Firefox has successfully quit.")
return
end if
set firefoxrunning to false
tell application "System Events"
set firefoxrunning to (name of processes) contains "Firefox"
end tell
if firefoxrunning is true then
my writetolog("After waiting a further 2 minutes, firefox is still running, so this script will now force it to close.")
do shell script "killall firefox"
--delay 5 * minutes
end if
end quitFirefox
---------------------------------------------------------------------------------------------------------
to check_for_another_instance()
--lets put back the random duration wait again
my writetolog("Now checking for another running instance of this script.")
set otherInstance to false
set fullFileName to ((path to documents folder as text) & "eyetvswitch.txt")
set posixFileName to POSIX path of fullFileName
set otherInstance to false
--display dialog fullFileName
--do shell script "rm -f " & quoted form of posixFileName
tell application "Finder"
if exists file fullFileName then
my writetolog("Switch file has been found to exist.")
try
set referenceNumber to open for access fullFileName with write permission
on error errText number errNum
if (errNum is not equal to 0) then
my writetolog("An error has occurred whilst trying to open switch for access(1): " & errNum & return & errText)
set otherInstance to true
end if
end try
else
my writetolog("Switch file has been found not to exist. it will be created and locked.")
try
set referenceNumber to open for access fullFileName with write permission
on error errText number errNum
if (errNum is not equal to 0) then
my writetolog("An error has whilst trying to open switch for access(2): " & errNum & return & errText)
set otherInstance to true
end if
end try
end if
if otherInstance = true then
my writetolog("Another instance of this script is running, so this instance will exit without any further action.")
return true
else
my writetolog("No other instance of this script is running, so it will continue and prevent others from running simultaneously.")
return false
end if
return otherInstance
end tell
end check_for_another_instance
---------------------------------------------------------------------------------------------------------------------------------------
on deleteSwitchFile()
set errNum to 0
if otherInstance = false then
try
close access referenceNumber
on error errText number errNum
if (errNum is not equal to 0) then
do shell script "rm -f " & quoted form of posixFileName
my writetolog("An error has occurred whilst closing switch file: " & errNum & return & errText)
end if
end try
if errNum = 0 then
my writetolog("Switch file successfully closed.")
end if
do shell script "rm -f " & quoted form of posixFileName
end if
end deleteSwitchFile
---------------------------------------------------------------------------------------------------------------------------------------

