Mac Developer: AppleScript Droplet to make main directory writable

Sometimes I like to be able to copy items to the top (root) level directory of a mounted local volume that contains its own Mac system installation. That’s normally forbidden, although I can’t remember the Mac OS X release that first applied that limitation. It was probably Panther (10.3) or Tiger (10.4), maybe even as late as Lion (10.5). Here is code I developed to make an AppleScript droplet to remove that limitation. Just create the application in Script Editor, drop a volume icon onto its icon, and give it appropriate approval using the Security & Privacy prefpane and/or whatever dialogs are presented when it is first run. It has some requirements for use under the current macOS environment: System Integrity Protection must be disabled, and the root user must be enabled, and (as as is true of most of my AppleScript solutions) it requires being used while logged in as an administrator. It should be noted that these requirements may significantly compromise built-in security measures, so this script is not for everyone.

property theList : {}
property theItem : {}
property temppath : "/private/tmp/"
property startnum : 0

on open the_items
	set thisuser to do shell script "id -un"
	my build_exec(the_items)
end open

on build_exec(the_items)
	repeat with the_item in the_items
		set the_item to the_item as alias
		set devnames to paragraphs of (do shell script "df -k | awk -F/ '/disk*/ {print $3}'")
		set t to {}
		repeat with s in devnames
			set t to t & (word 1 of s)
			--delay 1
		end repeat
		tell application "Finder"
			set the_answer to name of disk of (the_item as alias)
		end tell
		--display dialog the_answer as text
		set thisuser to do shell script "id -un"
		set target_name to replace_chars(the_answer, " ", "\\ ")
		set shell_script to "/bin/chmod 1775 /Volumes/" & target_name & "/;dseditgroup -o edit -a" & space & thisuser & space & "-t user wheel;/bin/sync"
		do shell script shell_script with administrator privileges
	end repeat
end build_exec

on replace_chars(this_text, _bad, _good)
	set AppleScript's text item delimiters to the _bad
	set the item_list to every text item of this_text
	set AppleScript's text item delimiters to the _good as string
	set this_text to the item_list as string
	set AppleScript's text item delimiters to ""
	return this_text
end replace_chars

on run
	set the_items to ((choose folder) as list)
	build_exec(the_items)
end run
Advertisements

Wrapping Desktop Icons with AppleScript

Desktop Wrap is an AppleScript Studio application I created several years ago to simplify keeping desktop icons organized in Mac OS X. The AppleScript solution provided here is an open and editable substitute for that application for use in the current macOS. You can use it to arrange the sorting and placement of desktop icons and to generate any number of custom AppleScripts to quickly set desktop icons into a custom arrangement. It also provides an example of integrated use of two scripting add-ons, Pashua and PrefsStorageLib.

Preparation:

  • Copy the entire AppleScript source from the code block at the bottom of the post, paste it into a window in Script Editor, save as an application anywhere you like using whatever name you choose. We’ll call it Wrap.app.
  • Download Pashua
  • Download PrefsStorageLib
  • Adjust Finder preferences to display Hard Disks and other desired items on the desktop.

Steps for installation and use:

  • Open and unlock the Security & Privacy prefpane in System preferences. Select the Privacy tab at the top. Select Accessibility from the list on the left, then drag Wrap.app into the list on the right. Then select Full Disk Access from the list on the left, then again drag Wrap.app into the list on the right.
  • Put Pashua.app from the Pashua distribution into your Applications folder.
  • Put the PrefsStorageLib.scptd from the PrefsStorageLib distribution into the Script Libraries folder of the Library folder of your Home folder. If the Script Libraries folder doesn’t exist in the Library folder of your Home folder, create it.
  • When you first launch Wrap.app, the system will prompt you to approve its use. Click OK. This may happen more than once, so click OK again if needed. When Pashua opens, the settings will be available in the window along with some guidance.
  • Note that there is an option for aligning icons starting from the left, so if you’ve ever wanted your Mac’s desktop to look more like a traditional Windows desktop, here’s your chance. Remember that new icons are always aligned toward the right, so if you want them to stay to the left, you’ll need to run the script again or move new icons manually.
  • Note that the last setting option allows you to place the generated icon wrapping commands onto the clipboard so you can create an AppleScript or AppleScript app that can immediately reset the icons with your desired settings.
  • After the AppleScript has been saved as an application, the path to me constructs become established so that the script can be run from within Script Editor. That might be useful if you want to customize the options to meet your own requirements.

Background

This AppleScript solution is a replacement for an app called Desktop Wrap that I created using AppleScript Studio back in the days when Mac OS X wasn’t very reliable for keeping icons in the same place on the desktop after logging out and back in. I worked out a set of simple but reliable calculations for organizing desktop icons to create Desktop Wrap, and I believe that that functionality combined with an example of the joint use of Pashua and PrefStorageLib might be interesting to some. There are a few reasons I never successfully migrated from AppleScript Studio to AppleScript Obj-C in order to provide a fully functional and current version of Desktop Wrap. The main ones are that transitioning to AppleScript Obj-C presented a fairly steep learning curve for me, and I didn’t really want to pay an annual membership to proliferate what for me has been mostly a hobby.

There’s a good chance that the solution is compatible with Sierra and High Sierra systems, although I haven’t tested (the security measures necessary for making it work with those systems will probably differ from those describe in the installation instructions). I wasn’t able to make it work in El Capitan, so if you figure out how, please let me know.

--begin AppleScript--
use AppleScript version "2.5"
use scripting additions
use script "PrefsStorageLib" version "1.0.0"

set customLocation to ""

try
	set pashuaLocation to getPashuaPath(customLocation)
	set dialogConfiguration to my getDialogConfiguration(pashuaLocation)
	set theResult to showDialog(dialogConfiguration, customLocation)
	if {} = theResult then
		display alert "Empty return value" message "It looks like Pashua had some problems using the window configuration." as warning
	else if cb of theResult is not "1" then
		assign value iconcol_count of theResult to key "iconcol_count"
		assign value firsticon_dist of theResult to key "firsticon_dist"
		assign value newcol_dist of theResult to key "newcol_dist"
		assign value const of theResult to key "const"
		assign value disksort of theResult to key "disksort"
		assign value nondisksort of theResult to key "nondisksort"
		assign value chk_default of theResult to key "chk_default"
		assign value chk1_default of theResult to key "chk1_default"
		assign value chkLeft_default of theResult to key "chkLeft_default"
		assign value clipsave of theResult to key "clipsave"
		
		tell application "Finder" to get the bounds of the window of the desktop
set {_width, _height} to (item 3 of the result) & (item 4 of the result)
		set iconcol_count to ((value for key "iconcol_count") as integer)
		set firsticon_dist to ((value for key "firsticon_dist") as integer)
		set newcol_dist to ((value for key "newcol_dist") as integer)
		set const to ((value for key "const") as integer)
		set disksort to value for key "disksort"
		set nondisksort to value for key "nondisksort"
		set chk_default to value for key "chk_default" --as text
		set chk1_default to value for key "chk1_default" --as text
		set chkLeft_default to value for key "chkLeft_default" --as text
		set clipsave to value for key "clipsave"
		
		set firstcol_calc to 0.5 * newcol_dist
		set firstcol_dist to (round firstcol_calc rounding up)
		set iconcol_calc to ((_height - firstcol_dist) / iconcol_count)
		set iconcol_dist to iconcol_calc as integer
		set heightdistance to (_height - iconcol_dist as integer)
		if chkLeft_default is "1" then
			set col1 to firstcol_dist
		else
			set col1 to _width - firstcol_dist
		end if
		set a to col1
		set b to firsticon_dist
		
		tell application "Finder"
			set itemlist to (every item of desktop) where its class is not disk
			set desktoplist to reverse of itemlist
			
			set startvollist to startup disk as list
			set startvolname to (startup disk as text)
			set disklist to ((every item of desktop) where its class is disk)
			
			set otherdisklist to {}
			
			repeat with i from 1 to count disklist
				set thestring to get disklist's item i as text
				if thestring is not startvolname then set otherdisklist to (otherdisklist & {item i of disklist})
			end repeat
			
			if disksort is in {"Name"} then
				set otherdisklist to sort otherdisklist by name
			else if disksort is in {"Modification Date"} then
				set otherdisklist to sort otherdisklist by modification date
			else if disksort is in {"Creation Date"} then
				set otherdisklist to sort otherdisklist by creation date
			else if disksort is in {"Kind"} then
				set otherdisklist to sort otherdisklist by kind
			end if
			if chk_default is "1" then
				set otherdisklist to reverse of otherdisklist
			end if
			
			if nondisksort is in {"Name"} then
				set desktoplist to sort itemlist by name
			else if nondisksort is in {"Modification Date"} then
				set desktoplist to sort itemlist by modification date
			else if nondisksort is in {"Creation Date"} then
				set desktoplist to sort itemlist by creation date
			else if nondisksort is in {"Kind"} then
				set desktoplist to sort itemlist by kind
			end if
			if chk1_default is "1" then
				set desktoplist to reverse of desktoplist
			end if
			
			set disklist to (startvollist & otherdisklist)
			set wholelist to (disklist & desktoplist)
			
			set this_item to 0
			
			log ("_height" & space & _height)
			log "iconcol_count" & space & iconcol_count
			log "firsticon_dist" & space & firsticon_dist
			log "newcol_dist" & space & newcol_dist
			log "iconcol_dist" & space & iconcol_dist
			log "const" & space & const
			log "newcol_dist + const" & space & (newcol_dist + const) as text
			log "chk_default" & space & chk_default as text
			log "chkLeft_default" & space & chkLeft_default as text
			log otherdisklist
			log clipsave
			repeat with i from 1 to count of wholelist
				set this_item to this_item + 1
				set diskcount to (count of wholelist)
				set desktarg to (item i of wholelist)
				if this_item / iconcol_count = (this_item / iconcol_count as integer) then
					if this_item = iconcol_count then
						set a to col1 - const
						set (desktop position of desktarg) to {a, b}
					else
						if chkLeft_default is "1" then
							(((round (this_item / iconcol_count) rounding up) * (newcol_dist - const)) - (firstcol_dist))
						else
							set a to ((_width - (round (this_item / iconcol_count) * (newcol_dist + const))) + (firstcol_dist))
						end if
						if b > heightdistance then
							set b to iconcol_dist
						else
							set b to (b + iconcol_dist)
						end if
						set (desktop position of desktarg) to {a, b}
					end if
				else if this_item / iconcol_count is not (this_item / iconcol_count as integer) then
					if this_item < iconcol_count then
						set a to col1 - const
						set (desktop position of desktarg) to {a, b}
						if b > heightdistance then
							set b to iconcol_dist
						else
							set b to (b + iconcol_dist)
						end if
					else if this_item = iconcol_count then
						set a to col1 - const
						set (desktop position of desktarg) to {a, b}
					else
						if chkLeft_default is "1" then
							set a to (((round (this_item / iconcol_count) rounding up) * (newcol_dist - const)) - (firstcol_dist))
						else
							set a to ((_width - ((round (this_item / iconcol_count) rounding up) * (newcol_dist + const))) + (firstcol_dist))
						end if
						if b > heightdistance - firstcol_dist then
							set b to firsticon_dist
						else
							set b to (b + iconcol_dist)
						end if
						set (desktop position of desktarg) to {a, b}
					end if
				end if
			end repeat
		end tell
		if clipsave = "1" then
			set the clipboard to "
use AppleScript version \"2.5\"
use scripting additions
tell application \"Finder\" to get the bounds of the window of the desktop
set {_width, _height} to (item 3 of the result) & (item 4 of the result)
set a to" & space & col1 & space & "
set b to" & space & firsticon_dist & space & "
tell application \"Finder\"
	
	set itemlist to (every item of desktop) where its class is not disk
	set desktoplist to reverse of itemlist
	set startvollist to startup disk as list
	set startvolname to (startup disk as text)
	set disklist to ((every item of desktop) where its class is disk)
	
	set otherdisklist to {}
	
	repeat with i from 1 to count disklist
		set thestring to get disklist's item i as text
		if thestring is not startvolname then set otherdisklist to (otherdisklist & {item i of disklist})
	end repeat
	
	if" & space & "\"" & disksort & "\"" & space & "is in {\"Name\"} then
		set otherdisklist to sort otherdisklist by name
	else if" & space & "\"" & disksort & "\"" & space & "is in {\"Modification Date\"} then
		set otherdisklist to sort otherdisklist by modification date
	else if" & space & "\"" & disksort & "\"" & space & "is in {\"Creation Date\"} then
		set otherdisklist to sort otherdisklist by creation date
	else if" & space & "\"" & disksort & "\"" & space & "is in {\"Kind\"} then
		set otherdisklist to sort otherdisklist by kind
	end if
	if" & space & "\"" & chk_default & "\"" & space & "is \"1\" then
		set otherdisklist to reverse of otherdisklist
	end if
		
	if" & space & "\"" & nondisksort & "\"" & space & "is in {\"Name\"} then
		set desktoplist to sort itemlist by name
	else if" & space & "\"" & nondisksort & "\"" & space & "is in {\"Modification Date\"} then
		set desktoplist to sort itemlist by modification date
	else if" & space & "\"" & nondisksort & "\"" & space & "is in {\"Creation Date\"} then
		set desktoplist to sort itemlist by creation date
	else if" & space & "\"" & nondisksort & "\"" & space & "is in {\"Kind\"} then
		set desktoplist to sort itemlist by kind
	end if
	if" & space & "\"" & chk1_default & "\"" & space & "is \"1\" then
		set desktoplist to reverse of desktoplist
	end if
		
	set disklist to (startvollist & otherdisklist)
	set wholelist to (disklist & desktoplist)	
	set this_item to 0
		
	repeat with i from 1 to count of wholelist
		set this_item to this_item + 1
		set diskcount to (count of wholelist)
		set desktarg to (item i of wholelist)
		
		if this_item /" & space & iconcol_count & space & "= (this_item /" & space & iconcol_count & space & "as integer) then
			if this_item =" & space & iconcol_count & space & "then
						set a to" & space & col1 & space & "-" & space & const & space & "	
						set (desktop position of desktarg) to {a, b}						
					else
						if" & space & chkLeft_default & space & "is \"1\" then
							(((round (this_item /" & space & iconcol_count & space & ") rounding up) * (" & space & newcol_dist & space & "-" & space & const & space & ")) - (" & space & firstcol_dist & space & "))
						else
							set a to ((_width - (round (this_item /" & space & iconcol_count & space & ") * (" & space & newcol_dist & space & "+" & space & const & space & "))) + (" & space & firstcol_dist & space & "))
						end if
						if b >" & space & heightdistance & space & "then
							set b to" & space & iconcol_dist & space & "
						else
							set b to (b +" & space & iconcol_dist & space & ")
						end if						
						set (desktop position of desktarg) to {a, b}						
					end if
				else if this_item /" & space & iconcol_count & space & "is not (this_item /" & space & iconcol_count & space & "as integer) then
					if this_item <" & space & iconcol_count & space & "then
						set a to" & space & col1 & space & "-" & space & const & space & "
						set (desktop position of desktarg) to {a, b}						
						if b >" & space & heightdistance & space & "then
							set b to" & space & iconcol_dist & space & "
						else
							set b to (b +" & space & iconcol_dist & space & ")
						end if
					else if this_item =" & space & iconcol_count & space & "then
							set a to" & space & col1 & space & "-" & space & const & space & "
						set (desktop position of desktarg) to {a, b}						
					else						
						if" & space & chkLeft_default & space & "is \"1\" then
							set a to (((round (this_item /" & space & iconcol_count & space & ") rounding up) * (" & space & newcol_dist & space & "-" & space & const & space & ")) - (" & space & firstcol_dist & space & "))
						else
							set a to ((_width - ((round (this_item /" & space & iconcol_count & space & ") rounding up) * (" & space & newcol_dist & space & "+" & space & const & space & "))) + (" & space & firstcol_dist & space & "))							
						end if						
						if b >" & space & heightdistance & space & "-" & space & firstcol_dist & space & "then
							set b to" & space & firsticon_dist & space & "
						else
							set b to (b +" & space & iconcol_dist & space & ")
						end if						
						set (desktop position of desktarg) to {a, b}						
					end if
				end if
			end repeat
		end tell"
		end if
	else
		display dialog "The dialog was closed without submitting the values"
	end if
	
on error errorMessage
	--	tell me to activate
	--display alert "An error occurred" message errorMessage as warning
end try


on getDialogConfiguration(pashuaLocation)
	prepare storage for (path to me) default values {iconcol_count:"15"}
	prepare storage for (path to me) default values {firsticon_dist:"55"}
	prepare storage for (path to me) default values {newcol_dist:"135"}
	prepare storage for (path to me) default values {const:"10"}
	prepare storage for (path to me) default values {disksort:"Kind"}
	prepare storage for (path to me) default values {nondisksort:"Kind"}
	prepare storage for (path to me) default values {chk_default:"Normal"}
	prepare storage for (path to me) default values {chk1_default:"Normal"}
	prepare storage for (path to me) default values {chkLeft_default:"Normal"}
	prepare storage for (path to me) default values {clipsave:"Normal"}
	
	
	set iconcol_count to value for key "iconcol_count"
	set firsticon_dist to value for key "firsticon_dist"
	set newcol_dist to value for key "newcol_dist"
	set const to value for key "const"
	set disksort to value for key "disksort"
	set nondisksort to value for key "nondisksort"
	set chk_default to value for key "chk_default"
	set chk1_default to value for key "chk1_default"
	set chkLeft_default to value for key "chkLeft_default"
	set clipsave to value for key "clipsave"
	
	if pashuaLocation is not "" then
		set img to "img.type = image
	          img.x = 435
	          img.y = 433
			  img.maxwidth = 128
			  img.tooltip = This is an element of type “image”
	          img.path = " & (POSIX path of pashuaLocation) & "/Contents/Resources/AppIcon@2.png" & return
	else
		set img to ""
	end if
	
	return "
# Set window title
*.title = Desktop Wrapper

txt.type = text
txt.default =*****************        Destop Wrap       **************[return][return][return][return][return][return][return][return]This script will let you make a custom arrangement for icons on the desktop that you can re-apply as needed. Desktop icons will be wrapped into columns according to these settings.[return][return]Experiment with the pop-up menus to get desired placements and spacing. Use View>Show View Options in Finder to customize icon size and icon label appearance.[return][return]Checkboxes:[return][return]Reverse sort disk icons - This will reverse the selected sorting order of disk icons.[return][return]Reverse sort non-disk icons - This will reverse the selected sorting order of non-disk icons.[return][return]Left-align icons - starts icon placement at the left edge of screen.[return][return]Copy static AppleScript to clipboard - places a copy of the generated icon placement commands on the clipboard so that an AppleScript can easily be created to quickly apply the custom icon arrangement.
txt.height = 276
txt.width = 320
txt.x = 340
txt.y = 44
txt.tooltip = This is an element of type “text”


# Add popup menus

iconcol_count.type = popup
iconcol_count.label = Number of icons per column
iconcol_count.width = 48
iconcol_count.option = 9
iconcol_count.option = 10
iconcol_count.option = 11
iconcol_count.option = 12
iconcol_count.option = 13
iconcol_count.option = 14
iconcol_count.option = 15
iconcol_count.option = 16
iconcol_count.option = 17
iconcol_count.option = 18
iconcol_count.option = 19
iconcol_count.option = 20
iconcol_count.default = " & space & iconcol_count & "
iconcol_count.tooltip = Set number of icons per column

firsticon_dist.type = popup
firsticon_dist.label = distance from top of screen to top of icons
firsticon_dist.width = 48
firsticon_dist.option = 43
firsticon_dist.option = 47
firsticon_dist.option = 51
firsticon_dist.option = 55
firsticon_dist.option = 59
firsticon_dist.option = 63
firsticon_dist.option = 67
firsticon_dist.option = 71
firsticon_dist.option = 75
firsticon_dist.option = 79
firsticon_dist.option = 83
firsticon_dist.default = " & space & firsticon_dist & "
firsticon_dist.tooltip = Set distance from top of screen to top of icons

newcol_dist.type = popup
newcol_dist.label = relative column width
newcol_dist.width = 80
newcol_dist.option = 60
newcol_dist.option = 65
newcol_dist.option = 70
newcol_dist.option = 75
newcol_dist.option = 80
newcol_dist.option = 85
newcol_dist.option = 95
newcol_dist.option = 105
newcol_dist.option = 115
newcol_dist.option = 125
newcol_dist.option = 135
newcol_dist.option = 145
newcol_dist.option = 155
newcol_dist.option = 165
newcol_dist.option = 175
newcol_dist.option = 185
newcol_dist.option = 195
newcol_dist.option = 205
newcol_dist.option = 215
newcol_dist.option = 225
newcol_dist.option = 235
newcol_dist.option = 245
newcol_dist.default = " & space & newcol_dist & "
newcol_dist.tooltip = Set relative column width


const.type = popup
const.label = relative horizontal column start position
const.width = 80
const.option = -30
const.option = -25
const.option = -20
const.option = -10
const.option = -5
const.option = 0
const.option = 5
const.option = 10
const.option = 15
const.option = 20
const.option = 25
const.option = 30
const.option = 35
const.option = 40
const.option = 45
const.option = 50
const.default = " & space & const & "
const.tooltip = Set relative horizontal column start position


disksort.type = popup
disksort.label = Sort disk icons by:
disksort.width = 300
disksort.option = Name
disksort.option = Kind
disksort.option = Modification date
disksort.option = Creation date
disksort.default =" & space & disksort & "
disksort.tooltip = Set sorting method

nondisksort.type = popup
nondisksort.label = Sort non-disk icons by:
nondisksort.width = 300
nondisksort.option = Name
nondisksort.option = Kind
nondisksort.option = Modification date
nondisksort.option = Creation date
nondisksort.default =" & space & nondisksort & "
nondisksort.tooltip = Set sorting method for non-disk icons

# Add 3 checkboxes
chk_default.rely = -18
chk_default.type = checkbox
chk_default.label = Reverse sort disk icons
chk_default.tooltip = Check to reverse sort disk icons
chk_default.default =" & space & chk_default & "

chk1_default.rely = -18
chk1_default.type = checkbox
chk1_default.label = Reverse sort non-disk icons
chk1_default.tooltip = Check to reverse sort non-disk icons
chk1_default.default =" & space & chk1_default & "

chkLeft_default.rely = -18
chkLeft_default.type = checkbox
chkLeft_default.label = Left align icons
chkLeft_default.tooltip = Check to align icons on left side of screen
chkLeft_default.default =" & space & chkLeft_default & "

clipsave.rely = -18
clipsave.type = checkbox
clipsave.label = Copy static AppleScript to clipboard
clipsave.tooltip = Check to copy a static AppleScript with current settings to the clipboard
clipsave.default =" & space & clipsave & "

# Add a cancel button with default label
cb.type = cancelbutton
cb.tooltip = This is an element of type “cancelbutton”

db.type = defaultbutton
db.tooltip = This is an element of type “defaultbutton” (which is automatically added to each window, if not included in the configuration)
" & img
end getDialogConfiguration

on showDialog(config, customLocation)
	
	-- Create path for temporary file
	set AppleScript's text item delimiters to ""
	set tmpfile to ((path to temporary items folder as text) & "Pashua_" & (characters 3 thru end of ((random number) as text)) as text)
	
	-- Write temporary file and fill it with the configuration string
	set fhandle to open for access tmpfile with write permission
	write (config as text) to fhandle as «class utf8»
	close access fhandle
	
	set posixtmpfile to POSIX path of tmpfile
	
	set pashua to getPashuaPath(customLocation)
	
	set pashuabinary to (POSIX path of pashua) & "Contents/MacOS/Pashua"
	
	-- Execute pashua and get the string returned
	set pashuaCall to quoted form of pashuabinary & " " & quoted form of posixtmpfile
	set pashuaResult to do shell script (pashuaCall)
	
	-- Delete the temporary file
	tell application "System Events" to delete file tmpfile -- silently and immediately delete the tempfile
	
	-- Check whether the dialog was submitted at all.
	-- If this is not the case, return an empty list
	if pashuaResult = "" then
		return {}
	end if
	
	set AppleScript's text item delimiters to return
	set resultLines to text items of pashuaResult
	set AppleScript's text item delimiters to ""
	set recordComponents to {}
	repeat with currentLine in resultLines
		set eqpos to offset of "=" in currentLine
		if eqpos is not 0 then
			set varKey to word 1 of currentLine
			try
				set varValue to (text (eqpos + 1) thru end of currentLine)
				-- Quote any quotation marks in varValue with a backslash.
				-- The proper way to do this would be a handler, but as
				-- all code for interfacing to Pashua should be as compact
				-- as possible, we rather do it inline
				set AppleScript's text item delimiters to "\""
				set textItems to every text item of varValue
				set AppleScript's text item delimiters to "\\\""
				set varValue to textItems as text
				set AppleScript's text item delimiters to ""
			on error
				set varValue to ""
			end try
			copy (varKey & ":\"" & varValue & "\"") to end of recordComponents
		end if
	end repeat
	
	-- Return the record we read from the tmpfile
	set AppleScript's text item delimiters to ", "
	set resultList to (run script "return {" & (recordComponents as text) & "}")
	set AppleScript's text item delimiters to {""}
	return resultList
	
end showDialog

on getPashuaPath(customFolder)
	set myself to (path to me as text)
	tell application "Finder"
		-- Try to find it by application file id
		if exists (application file id "net.bluem.pashua" as alias as text) then
			return POSIX path of (application file id "net.bluem.pashua" as alias as string)
		end if
		error "Could not find Pashua.app" & return & return & "Try double-clicking the Pashua.app application file even though the ensuing dialog box says it's not meant to be used that way."
	end tell
end getPashuaPath
--end AppleScript--


AppleScript for Drop-converting Blu-ray Rips for Streaming

Requirements

  • ffmpeg (install using default Homebrew configuration)
  • One or more .mkv  or .m2ts files ripped from Blu-ray using MakeMKV.

This method preserves 5.1 audio, so if you use a home streaming environment such as Apple TV, the audio can be delivered in Surround Sound.

You can use Rob Griffith’s Blu-ray ripping recommendations to get a compatible .mkv file. I have been able to get the necessary source file by analyzing the Blu-ray disc (you must have a Blu-ray capable CD/DVD disc reader, such as a Buffalo, attached to your Mac) using MakeMKV and selecting the title with the greatest number of chapters and including a 7.1/5.1 audio stream for ripping. If you back up the entire Blu-ray disc, you should also be able to locate the correct .m2ts file and drop it instead. The output file of this AppleScript application is created in the same folder as the dropped source file(s). The process will open a Terminal window which displays the progress of the conversion process. Until you’re used to the process, it’s probably a good idea to press ⌘-. (command – period) while the Terminal window is frontmost a minute or two into the conversion process so that you can audition the output video to be sure it’s what you wanted. If it’s not the right title, or doesn’t contain the correct audio program, you can adjust the MakeMKV ripping configuration settings and try again.

Create the AppleScript applet by copying and pasting the following code into the Script Editor included with a recent Mac system version such as Mojave or High Sierra and saving as an application. You may then simply drag and drop one or more .mkv files created using MakeMKV. I rarely do more than one at a time, but if your Mac has enough disk space to accommodate several, and you have time to let your Mac complete the process, you can convert multiple files with a single drop.

The AppleScript includes a construct for determining the proper crop values to be applied for the version. It is based on determining the video dimensions at a point that is 44 seconds into the video. If you encounter incorrect crop values for the converted video, you may wish to change the value to a higher or lower number. The adjustment should be made to the script by changing the default value specified (“-t 44”) to something else such as (“-t 25”).

--begin AppleScript--


property temppath : "/private/tmp/"
property startnum : 0
property newline : ASCII character 10
property tmpfile : "/tmp/execme.command"

on open the_items
my video_convert(the_items)
end open

on video_convert(the_items)
set theshellscript to ""
repeat with the_item in the_items
set the_item to the_item as alias
try
tell application "Finder"
set sost to (container of the_item) as string
end tell
set pos_filepath to POSIX path of sost
end try
set this_filepath to (the_item as string)

if last character of this_filepath is ":" then
tell me to set it_is_a_folder to true
else
set it_is_a_folder to false
end if
set thesourcename to (name of (info for the_item))
set namepart to (name extension of (info for the_item))
set the_source_file to POSIX path of this_filepath
set newname to replace_chars(thesourcename, namepart, "m4v")
set autocrop to (do shell script "/usr/local/bin/ffmpeg -i" & space & (quoted form of the_source_file) & space & "-t 44 -vf cropdetect -f null - 2>&1 | awk '/crop/ { print $NF }' | tail -1")
try
set theshellscript to the theshellscript & "/usr/local/bin/ffmpeg" & space & "-i" & space & (quoted form of the_source_file) & space & "-pix_fmt yuv420p -c:v libx264 -preset slow -tune film -vf \"" & autocrop & "\" -c:a ac3 -ac 6 -ab 640k -crf 18 -x264opts keyint=40:bitrate=2400:qpmin=8:qpmax=28:qpstep=4" & space & (quoted form of (pos_filepath & newname)) & ";/bin/echo '

==========================

" & newname & space & "FINISHED!" & "

==========================

';"
on error onerr
activate
display dialog onerr
end try

end repeat
set theshellscript to theshellscript & "mv" & space & (quoted form of tmpfile) & space & (quoted form of (POSIX path of (path to trash)))
do shell script "echo " & quoted form of theshellscript & " > " & tmpfile
repeat
try
do shell script "chmod +x " & tmpfile
do shell script "open -a Terminal.app" & space & tmpfile
exit repeat
on error
delay 1
end try
end repeat
end video_convert

on replace_chars(this_text, _bad, _good)
set AppleScript's text item delimiters to the _bad
set the item_list to every text item of this_text
set AppleScript's text item delimiters to the _good as string
set this_text to the item_list as string
set AppleScript's text item delimiters to ""
return this_text
end replace_chars

on run
set the_items to ((choose folder) as list)
video_convert(the_items)
end run

--end AppleScript--

 

AppleScript to delete APFS Local Snapshots

Here’s an AppleScript I’ve been using to view listings for local snapshots then delete unwanted ones for my APFS boot volume in High Sierra. You’ll be prompted for your admin password for deleting snapshots.

set IDList to paragraphs of (do shell script "tmutil listlocalsnapshots /")
set BL_1 to {}
repeat with i in IDList
set end of BL_1 to ((word 2 of i & "-" & word 3 of i & "-" & word 4 of i & "-" & word 5 of i) as text)
end repeat
if (count of BL_1) is not 0 then
choose from list BL_1 with prompt "Select snapshots to delete. Select as many as desired. Enter administrator password when prompted." with multiple selections allowed
set theSelection to result
if (count of theSelection) is not 0 then
repeat with theItem in theSelection
do shell script ("tmutil deletelocalsnapshots" & space & theItem) with administrator privileges
end repeat
end if
else
display dialog "There appear to be no snapshots to delete." buttons {"OK"} default button 1
end if

 

AppleScript: Clipboard to text file on Desktop

Clipping files created by dragging a text selection to the desktop are very useful, but sometimes it’s more beneficial to create a text file. Here’s an AppleScript that does that. The script is most conveniently used by activating it with a keyboard shortcut or other shortcut rather than having to find it in your Script menu. One way to make it quickly accessible is to place it in the toolbar of Finder windows. The way I run it is to use ⇧⌃⌥L (Shift-Ctrl-Opt L) in Keyboard Maestro. You could also put it into the Script Menu and launch it using Daniel Jalkut’s FastScripts, or you could even create a System Service from the AppleScript using Automator and configure the keyboard trigger in the Shortcuts tab of the Keyboard sysprefpane (System Preferences> Keyboard). Whatever way you choose to run it, all you need to do is make sure the text you want turned into a file is copied to your clipboard first. The Finder window toolbar and Automator System Service methods are built into the Mac system and are completely free. Keyboard Maestro and FastScripts are commercial automation solutions, but both are well worth the investment.

 

property temppath : "/private/tmp/"

property startnum : 0

property clipname : "clip"

property add_on : ".txt"

activate

set clipname to (text returned of (display dialog "Enter a description of the clipboard text that will be used to name the file:" default answer clipname))

set rootpath to ((path to desktop) as Unicode text)

set thepathname to rootpath & clipname

set pos_filepath to POSIX path of rootpath as Unicode text

set dest_file to (quoted form of (temppath & clipname & add_on))

do shell script "pbpaste >" & space & dest_file

tell application "Finder"

set basename to clipname

repeat

try

set name_target to alias (rootpath & clipname & add_on) --file name is already taken at destination if no error occurs.

set startnum to startnum + 1

set clipname to text returned of (display dialog"A file with that name already exists. You may enter a new name or cancel."default answer (basename & startnum))

on error _error

set startnum to 0

if _error contains clipname then --if target is clear and source file can be copied from temp directory

tell me to do shell script"ditto" & space & dest_file & space & (quoted form of (pos_filepath & clipname & add_on))

exit repeat

else -- if action is cancelled by user

exit repeat

end if

end try

end repeat

end tell

Keysinkers

If you ever get tired of holding down a key or a combination of keys to make your Mac observe any of several modes or behaviors at startup, you might be interested in preparing a few keysinkers. For example, you could use a couple of keysinkers to boot into Recovery Mode while you go fetch a cup of coffee. You can readily construct keysinkers using cheap fishing tackle (line sinkers) and Sugru or Oogoo.

Just adapt the bottom of the sinker to give it a flat surface and make it lean enough to accommodate the angle of your particular keyboard’s surface. I’ve been able to compromise the angle so that my keysinkers can remain stable for different degrees of keyboard tilt.

Illustration of keysinkers on Mac keyboard keys