Latest Entries »

Cycling and me

I am becoming a bit of a cycling enthusiast. Rather surprising at age 48. It’s not my first encounter with cycling, but it seems to have become my most intimate. I think it is about time to get something on the record. It is just my most recent episode of cycling. There have been many others over years.

Advertisements

Flower

DSCN0075 by myredroom
DSCN0075, a photo by myredroom on Flickr.

A new pic from my new cam.

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
---------------------------------------------------------------------------------------------------------------------------------------