Script to open all Reading List items in Safari and then import to EagleFiler

Hello all,
I have assembled an AppleScript to help automate the process of importing links from Safari’s Reading List to EagleFiler. I would appreciate feedback and any suggestions for improvement–it hasn’t failed on me yet, but I am especially interested in code or strategies to make it more robust.

Background: I often save dozens if not hundreds of links to the Reading List in Safari (mostly on iOS) over the course of a few months. To get these links into my archive library in EagleFiler, I then periodically open 10-20 in Safari tabs at a time and use the standard “Import Safari Tabs” AppleScript. This is a slow and rather labor intensive process. At various times, I have tried opening 30, 40, 50, …, 100 tabs at a time, but for me these large imports have not been uniformly successful (I haven’t looked into the problem in detail to know why the large import fails).

Assumptions: The script is saved in the EagleFiler script folder, Safari is open but all windows are closed***, EagleFiler is open and is the active application.

***This script works only on visible Safari windows, so if you have open windows that you do not want to import then just minimize them to the dock.

Workflow for this script:

  1. EagleFiler -> AppleScript menu item -> Import Safari Reading List (or whatever you named it)
  2. Safari will be selected as the active application and windows will start opening each with 12 tabs (if there are less then 12 links in the Reading List then that number of tabs will open in only one window)
  3. A dialog box will open and ask that you wait for all of the tabs to finish loading. This is also where you can review and close any unwanted tabs. Click “Continue” when you are ready to proceed.
  4. EagleFiler is selected as the active application. The script will loop through each window and import up to 12 tabs at a time. You can open the activity window to monitor the progress.

Additional steps: Unfortunately, this script does not clean up after itself. You will still need to close all of the Safari windows (this should be trivial, but I didn’t fit it in today). You will also need to open the Reading List in Safari and “Clear All Items …” (this seems much less trivial based on needing to edit the plist file?).

Citation links to the material that I used to assemble this script can be found in the header.


-- import-safari-reading-list
-- Modified From:
(*
http://c-command.com/scripts/eaglefiler/import-safari-tabs
https://workentin.wordpress.com/2016/01/10/automatically-open-all-reading-list-items/
https://discussions.apple.com/message/28304986#28304986
http://stackoverflow.com/questions/11706171/how-to-open-a-new-window-and-multiple-urls-in-safari-with-apple-script
https://gist.github.com/edenwaith/2213a764ccb091d6a03989f238efb63f
https://developer.apple.com/library/content/documentation/LanguagesUtilities/Conceptual/MacAutomationScriptingGuide/ManipulateListsofItems.html
https://discussions.apple.com/thread/6730717?start=0&tstart=0
https://developer.apple.com/library/content/documentation/AppleScript/Conceptual/AppleScriptLangGuide/reference/ASLR_operators.html
*)
-- Last Modified: 2017-02-06

-- Sets tfile to look at Safari's bookmarks file, which contains the Reading List
set tfile to (path to library folder from user domain as text) & "Safari:bookmarks.plist"

-- Creats a blank list, which will later have Reading List items added to it.
set theURLs to {}

-- System Events lets you access the content of a plist
tell application "System Events"
	
	-- Check each item in the Safari bookmarks file 
	repeat with i in (property list items of property list item "Children" of property list file tfile)
		tell i to try
			
			-- Check the item to see if it is a part of the Reading List
			if value of property list item "Title" = "com.apple.ReadingList" then
				repeat with thisDict in (get value of property list item "Children")
					
					-- Add item to list of URLs to open
					tell thisDict to set end of theURLs to its URLString
				end repeat
				exit repeat
			end if
		end try
	end repeat
end tell

-- Get the length of the URL list
set listSize to count of theURLs

-- Script switches to Safari
activate application "Safari"

tell application "Safari"
	-- This is the main loop
	-- Loop opens as many new windows each with 12 tabs as necessary
	-- Found that importing more than 12 tabs in EagleFiler can sometimes fail unexpectedly
	set i to 1
	repeat until i is greater than listSize
		if i is greater than listSize then
			exit repeat
		else
			-- Opens a new window with one URL
			set {firstURL} to {item i of theURLs}
			make new document at end of documents with properties {URL:firstURL}
			set i to i + 1
		end if
		
		repeat with j from 1 to 11
			if i is greater than listSize then
				exit repeat
			else
				-- Opens additional tabs (up to total of 12) in the new Window
				tell window 1
					set {theURL} to {item i of theURLs}
					make new tab at end of tabs with properties {URL:theURL}
				end tell
				set i to i + 1
			end if
			
		end repeat
	end repeat
end tell

-- Open a dialog box to allow tab trimming before import
-- Also acts as a delay to allow URLs to open
set answer to ""
repeat while answer is equal to ""
	display dialog "Check that all Safari tabs have loaded. Close any tabs that you do not want to import." buttons {"Continue"} default button 1
	set answer to button returned of result
end repeat

activate application "EagleFiler"

-- Import to EagleFiler one window at a time
tell application "Safari"
	set windowList to (every window whose visible is true)
	repeat with windowVariable in windowList
		set _urls to URL of every tab of windowVariable
		
		tell application "EagleFiler"
			import URLs _urls
		end tell
		
	end repeat
end tell

Import Safari Reading List.scpt.zip (11 KB)

This is cool! It’s unfortunate that Apple doesn’t have an API for accessing the reading list directly.

Has something changed?
I see this script is now two years old, did something change? I can’t get it to work. Followed it to a “T” and no errors, nothing…

It looks like Apple changed the file format, so the script isn’t failing but it isn’t finding any data in the place it was looking.

Well that’s a bummer, but the good news it isn’t me, it’s the script :wink:

Workaround

So … this quit working automatically a while ago … I have a “temp” semi-manual, workaround, but I haven’t investigated fully yet to see if this can be fixed back to fully automatic.

When I want to run the workaround script,

  1. I now manually copy the “Bookmarks.plist” from the Library->Safari folder to my Downloads folder
  2. I have changed the following line in the script to find the Bookmarks.plist file in my downloads file: set tfile to “/Users/<username>
    /Downloads/Bookmarks.plist”
  3. You will need to change that line to match your user name.
  4. Run the script as previously documented.

I think that this is a permissions issue accessing the Library folder, but again, I haven’t taken the time to fully troubleshoot as the workaround is … working for me right now. Thanks

-- import-safari-reading-list
-- Modified From:
(*
http://c-command.com/scripts/eaglefiler/import-safari-tabs
https://workentin.wordpress.com/2016/01/10/automatically-open-all-reading-list-items/
https://discussions.apple.com/message/28304986#28304986
http://stackoverflow.com/questions/11706171/how-to-open-a-new-window-and-multiple-urls-in-safari-with-apple-script
https://gist.github.com/edenwaith/2213a764ccb091d6a03989f238efb63f
https://developer.apple.com/library/content/documentation/LanguagesUtilities/Conceptual/MacAutomationScriptingGuide/ManipulateListsofItems.html
https://discussions.apple.com/thread/6730717?start=0&tstart=0
https://developer.apple.com/library/content/documentation/AppleScript/Conceptual/AppleScriptLangGuide/reference/ASLR_operators.html
*)
--Last Modified: 2019-08-06
-- Updated forum post with workaround

-- Modified: 2017-02-06
-- Sets tfile to look at Safari's bookmarks file, which contains the Reading List
--set tfile to (path to library folder from user domain as text) & "Safari:bookmarks.plist"

set tfile to "/Users/***<username>***/Downloads/Bookmarks.plist"

-- Creats a blank list, which will later have Reading List items added to it.
set theURLs to {}

-- System Events lets you access the content of a plist
tell application "System Events"
	
	-- Check each item in the Safari bookmarks file 
	repeat with i in (property list items of property list item "Children" of property list file tfile)
		tell i to try
			
			-- Check the item to see if it is a part of the Reading List
			if value of property list item "Title" = "com.apple.ReadingList" then
				repeat with thisDict in (get value of property list item "Children")
					
					-- Add item to list of URLs to open
					tell thisDict to set end of theURLs to its URLString
				end repeat
				exit repeat
			end if
		end try
	end repeat
end tell

-- Get the length of the URL list
set listSize to count of theURLs

-- Script switches to Safari
activate application "Safari"

tell application "Safari"
	-- This is the main loop
	-- Loop opens as many new windows each with 12 tabs as necessary
	-- Found that importing more than 12 tabs in EagleFiler can sometimes fail unexpectedly
	set i to 1
	repeat until i is greater than listSize
		if i is greater than listSize then
			exit repeat
		else
			-- Opens a new window with one URL
			set {firstURL} to {item i of theURLs}
			make new document at end of documents with properties {URL:firstURL}
			set i to i + 1
		end if
		
		repeat with j from 1 to 11
			if i is greater than listSize then
				exit repeat
			else
				-- Opens additional tabs (up to total of 12) in the new Window
				tell window 1
					set {theURL} to {item i of theURLs}
					make new tab at end of tabs with properties {URL:theURL}
				end tell
				set i to i + 1
			end if
			
		end repeat
	end repeat
end tell

-- Open a dialog box to allow tab trimming before import
-- Also acts as a delay to allow URLs to open
set answer to ""
repeat while answer is equal to ""
	display dialog "Check that all Safari tabs have loaded. Close any tabs that you do not want to import." buttons {"Continue"} default button 1
	set answer to button returned of result
end repeat

activate application "EagleFiler"

-- Import to EagleFiler one window at a time
tell application "Safari"
	set windowList to (every window whose visible is true)
	repeat with windowVariable in windowList
		set _urls to URL of every tab of windowVariable
		
		tell application "EagleFiler"
			import URLs _urls
		end tell
		
	end repeat
end tell

(*

set tfile to (path to library folder from user domain as text) & "Safari:bookmarks.plist"

*)

Great! I don’t know why, when I searched the file before, “com.apple.ReadingList” didn’t come up. That’s why I thought the format had changed. But now I see it.

My guess is that the permissions issue is that, with macOS 10.14 and later, the ~/Library/Safari folder is protected. However, the script should be able to access it if you give your script-running app Full Disk Access.

Thanks for looking at this Michael. I am still not able to solve the permissions issue, but I have automated the workaround. This could still use some cleaning up, but at least it works as I expect again. Please note where you need to enter your own <username> in the file/folder paths.



-- import-safari-reading-list
-- Modified From:
(*
http://c-command.com/scripts/eaglefiler/import-safari-tabs
https://workentin.wordpress.com/2016/01/10/automatically-open-all-reading-list-items/
https://discussions.apple.com/message/28304986#28304986
http://stackoverflow.com/questions/11706171/how-to-open-a-new-window-and-multiple-urls-in-safari-with-apple-script
https://gist.github.com/edenwaith/2213a764ccb091d6a03989f238efb63f
https://developer.apple.com/library/content/documentation/LanguagesUtilities/Conceptual/MacAutomationScriptingGuide/ManipulateListsofItems.html
https://discussions.apple.com/thread/6730717?start=0&tstart=0
https://developer.apple.com/library/content/documentation/AppleScript/Conceptual/AppleScriptLangGuide/reference/ASLR_operators.html
*)
--Last Modified: 2019-08-20
-- Added code to copy Bookmarks.plist from Library to Downloads folder to automate the workaround
-- I tried to find a solution to the permissions issue, but ... I am still troubleshooting.
-- Added code to extend the timeout to 300 seconds, but I have not verified that this works.

--Modified: 2019-08-06
-- Updated forum post with workaround

-- Modified: 2017-02-06
-- Sets tfile to look at Safari's bookmarks file, which contains the Reading List
-- set tfile to (path to library folder from user domain as text) & "Safari:bookmarks.plist"

try
	tell application "Finder"
		delete file "Macintosh HD:Users:**<username>**:Downloads:Bookmarks.plist"
	end tell
end try

tell application "Finder"
	copy file "Macintosh HD:Users:**<username>**:Library:Safari:Bookmarks.plist" to folder "Macintosh HD:Users:**<username>**:Downloads"
end tell

set tfile to "/Users/**<username>**/Downloads/Bookmarks.plist"

-- Creats a blank list, which will later have Reading List items added to it.
set theURLs to {}

-- System Events lets you access the content of a plist
tell application "System Events"
	
	-- Check each item in the Safari bookmarks file 
	repeat with i in (property list items of property list item "Children" of property list file tfile)
		tell i to try
			
			-- Check the item to see if it is a part of the Reading List
			if value of property list item "Title" = "com.apple.ReadingList" then
				repeat with thisDict in (get value of property list item "Children")
					
					-- Add item to list of URLs to open
					tell thisDict to set end of theURLs to its URLString
				end repeat
				exit repeat
			end if
		end try
	end repeat
end tell

-- Get the length of the URL list
set listSize to count of theURLs

-- Script switches to Safari
activate application "Safari"

tell application "Safari"
	-- This is the main loop
	-- Loop opens as many new windows each with 12 tabs as necessary
	-- Found that importing more than 12 tabs in EagleFiler can sometimes fail unexpectedly
	set i to 1
	repeat until i is greater than listSize
		
		with timeout of 300 seconds
			
			if i is greater than listSize then
				exit repeat
			else
				-- Opens a new window with one URL
				set {firstURL} to {item i of theURLs}
				make new document at end of documents with properties {URL:firstURL}
				set i to i + 1
			end if
			
			repeat with j from 1 to 11
				if i is greater than listSize then
					exit repeat
				else
					-- Opens additional tabs (up to total of 12) in the new Window
					tell window 1
						set {theURL} to {item i of theURLs}
						make new tab at end of tabs with properties {URL:theURL}
					end tell
					set i to i + 1
				end if
				
			end repeat
		end timeout
	end repeat
end tell

-- Open a dialog box to allow tab trimming before import
-- Also acts as a delay to allow URLs to open
set answer to ""
repeat while answer is equal to ""
	display dialog "Check that all Safari tabs have loaded. Close any tabs that you do not want to import." buttons {"Continue"} default button 1
	set answer to button returned of result
end repeat

activate application "EagleFiler"

-- Import to EagleFiler one window at a time
tell application "Safari"
	set windowList to (every window whose visible is true)
	repeat with windowVariable in windowList
		set _urls to URL of every tab of windowVariable
		
		tell application "EagleFiler"
			import URLs _urls
		end tell
		
	end repeat
end tell

tell application "Finder"
	delete file "Macintosh HD:Users:**<username>**:Downloads:Bookmarks.plist"
end tell


Hello all,

I just tested this AppleScript on a new M1 MacBook Air. It is working as expected.

If you read back through this thread, I ran into permission issues because of some changes that Apple had made. I worked around those by turning this script into an app and then giving the app elevated permissions. Security & Privacy -> Privacy settings -> Automation and Full Disk Access.

Below is the most recent code. This script includes hard coded paths, so be sure to run a find and replace on <username> before trying it out. If you have changed the name of your storage drive from “Macintosh HD” then you will also need to update that.

-- import-safari-reading-list
-- Modified From:
(*
http://c-command.com/scripts/eaglefiler/import-safari-tabs
https://workentin.wordpress.com/2016/01/10/automatically-open-all-reading-list-items/
https://discussions.apple.com/message/28304986#28304986
http://stackoverflow.com/questions/11706171/how-to-open-a-new-window-and-multiple-urls-in-safari-with-apple-script
https://gist.github.com/edenwaith/2213a764ccb091d6a03989f238efb63f
https://developer.apple.com/library/content/documentation/LanguagesUtilities/Conceptual/MacAutomationScriptingGuide/ManipulateListsofItems.html
https://discussions.apple.com/thread/6730717?start=0&tstart=0
https://developer.apple.com/library/content/documentation/AppleScript/Conceptual/AppleScriptLangGuide/reference/ASLR_operators.html
*)
--Last Modified: 2021-03-11
-- Updated forum post with details about permissions and move to M1 MacBook Air.

--Modified: 2019-08-20
-- Added code to copy Bookmarks.plist from Library to Downloads folder to automate the workaround
-- I tried to find a solution to the permissions issue, but ... I am still troubleshooting.

--Modified: 2019-08-06
-- Updated forum post with workaround

-- Modified: 2017-02-06
-- Sets tfile to look at Safari's bookmarks file, which contains the Reading List
-- set tfile to (path to library folder from user domain as text) & "Safari:bookmarks.plist"

try
    tell application "Finder"
        delete file "Macintosh HD:Users:<username>:Downloads:Bookmarks.plist"
    end tell
end try

tell application "Finder"
    copy file "Macintosh HD:Users:<username>:Library:Safari:Bookmarks.plist" to folder "Macintosh HD:Users:<username>:Downloads"
end tell

set tfile to "/Users/<username>/Downloads/Bookmarks.plist"

-- Creats a blank list, which will later have Reading List items added to it.
set theURLs to {}

-- System Events lets you access the content of a plist
tell application "System Events"
    
    -- Check each item in the Safari bookmarks file 
    repeat with i in (property list items of property list item "Children" of property list file tfile)
        tell i to try
            
            -- Check the item to see if it is a part of the Reading List
            if value of property list item "Title" = "com.apple.ReadingList" then
                repeat with thisDict in (get value of property list item "Children")
                    
                    -- Add item to list of URLs to open
                    tell thisDict to set end of theURLs to its URLString
                end repeat
                exit repeat
            end if
        end try
    end repeat
end tell

-- Get the length of the URL list
set listSize to count of theURLs

-- Script switches to Safari
activate application "Safari"

tell application "Safari"
    -- This is the main loop
    -- Loop opens as many new windows each with 12 tabs as necessary
    -- Found that importing more than 12 tabs in EagleFiler can sometimes fail unexpectedly
    set i to 1
    repeat until i is greater than listSize
        
        with timeout of 300 seconds
            
            if i is greater than listSize then
                exit repeat
            else
                -- Opens a new window with one URL
                set {firstURL} to {item i of theURLs}
                make new document at end of documents with properties {URL:firstURL}
                set i to i + 1
            end if
            
            repeat with j from 1 to 11
                if i is greater than listSize then
                    exit repeat
                else
                    -- Opens additional tabs (up to total of 12) in the new Window
                    tell window 1
                        set {theURL} to {item i of theURLs}
                        make new tab at end of tabs with properties {URL:theURL}
                    end tell
                    set i to i + 1
                end if
                
            end repeat
        end timeout
    end repeat
end tell

-- Open a dialog box to allow tab trimming before import
-- Also acts as a delay to allow URLs to open
set answer to ""
repeat while answer is equal to ""
    display dialog "Check that all Safari tabs have loaded. Close any tabs that you do not want to import." buttons {"Continue"} default button 1
    set answer to button returned of result
end repeat

activate application "EagleFiler"

-- Import to EagleFiler one window at a time
tell application "Safari"
    set windowList to (every window whose visible is true)
    repeat with windowVariable in windowList
        set _urls to URL of every tab of windowVariable
        
        tell application "EagleFiler"
            import URLs _urls
        end tell
        
    end repeat
end tell

tell application "Finder"
    delete file "Macintosh HD:Users:<username>:Downloads:Bookmarks.plist"
end tell