Save multiple MacScripter topics as WebArchive


(* 
   The script automatically creats folder "MacScripter Topics" and numerated subfolder in it,
   adds to name of topic its MacScripter index,
   saves the (10, or more) topics as webarchive.
   Made some improvments to stabilize the script,
   display dialog is replaced with display notification to not interrupt the process
   
   Written by KniazidisR
	I used here the  ideas and snippets from Fredrik71 and
	Shane Stanley <sstanley@myriad-com.com.au>
*)

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions
use framework "Foundation"
use framework "AppKit"
use framework "WebKit"
property loadingWaitMaximumSeconds : 10
property thePath : missing value
property indexOfNewestTopic : 32247 -- indicate here the Topic you consider as newest
set indexofLastTopics to 1 -- indexOfNewestTopic - 9

set homeFolderHFS to (path to home folder from user domain) as text
tell application "Finder" to if not (exists (homeFolderHFS & "MacScripter Topics:")) then make new folder at documentsFolderHFS with properties {name:"MacScripter Topics"}
set macscripterFolder to alias (homeFolderHFS & "MacScripter Topics:")

set destinationFolderHFS to (macscripterFolder as text) & indexofLastTopics & "-" & indexOfNewestTopic
tell application "Finder" to if not (exists destinationFolderHFS) then make new folder at (macscripterFolder as text) with properties {name:("" & indexofLastTopics & "-" & indexOfNewestTopic)}
set destinationFolderPath to POSIX path of (alias destinationFolderHFS)

tell application "Safari" to activate

repeat with i from indexOfNewestTopic to indexofLastTopics by -1
	
	-- open location
	tell application "Safari" to make new document with properties ¬
		{URL:("https://www.macscripter.net/viewtopic.php?id=" & i)}
	-- check page full loading
	set isLoaded to my waitSafariWebPageLoading(loadingWaitMaximumSeconds)
	
	if isLoaded then
		if document "Info / MacScripter" of application "Safari" exists then
			-- badTopic URL : "The link you followed is incorrect or outdated."
			close document "Info / MacScripter" of application "Safari" saving no
		else
			-- Tell Safari to get the name and URL of document 1
			tell application "Safari" to tell document 1 to set {theName, theURL} to {name, URL}
			-- Correct the name string.
			set theName to (my stringOffsetBeginOrEnd:theName withOffset:"/" withOption:"end")
			-- Output path of the file.
			set thePath to destinationFolderPath & i & ". " & theName & " .webarchive"
			-- Run the handler.
			(my archivePage:theURL toPath:thePath)
			-- Following 2 lines is optional. Remove them to speed up the process
			display notification "Webpage saved"
			delay 7 -- this is to to allow switching notification
		end if
		-- close current document
		tell application "Safari" to close documents saving no
	end if
	
end repeat

on waitSafariWebPageLoading(loadingWaitMaximumSeconds as integer)
	set lineChangingChars to {linefeed, return, character id 11, character id 12, character id 133, character id 8232, character id 8233}
	set {ATID, htmlEnding} to {AppleScript's text item delimiters, ""}
	tell application "Safari"
		repeat 100 * loadingWaitMaximumSeconds times
			delay 0.1
			set AppleScript's text item delimiters to {"<", ">"}
			try
				copy text item -2 of (get source of front document) to htmlEnding
			end try
			set AppleScript's text item delimiters to lineChangingChars
			set htmlEnding to text items of htmlEnding
			set AppleScript's text item delimiters to ""
			set htmlEnding to "<" & htmlEnding & ">"
			if htmlEnding is "</html>" then exit repeat
		end repeat
	end tell
	set AppleScript's text item delimiters to ATID
	if htmlEnding is "</html>" then return true
	display notification "The webpage loading failed"
	return false
end waitSafariWebPageLoading

on archivePage:thePageURL toPath:aPath
	set my thePath to aPath -- store path for use later
	my performSelectorOnMainThread:"setUpWebViewForPage:" withObject:thePageURL waitUntilDone:true
end archivePage:toPath:

on setUpWebViewForPage:thePageURL
	-- needs to be done on the main thread
	-- make a WebView
	set theView to current application's WebView's alloc()'s initWithFrame:{origin:{x:0, y:0}, |size|:{width:100, height:100}}
	-- tell it to call delegate methods on me
	theView's setFrameLoadDelegate:me
	-- load the page
	theView's setMainFrameURL:thePageURL
end setUpWebViewForPage:

-- called when the WebView loads a frame
on WebView:aWebView didFinishLoadForFrame:webFrame
	-- the main frame is our interest
	if webFrame = aWebView's mainFrame() then
		-- get the data and write it to file
		set theArchiveData to webFrame's dataSource()'s webArchive()'s |data|()
		theArchiveData's writeToFile:thePath atomically:true
		-- display notification "The webarchive was saved"
	end if
end WebView:didFinishLoadForFrame:

on WebView:WebView didFailLoadWithError:theError forFrame:webFrame
	-- got an error, bail
	WebView's stopLoading:me
	display notification "The webarchive was not saved"
end WebView:didFailLoadWithError:forFrame:

on stringOffsetBeginOrEnd:theString withOffset:theChar withOption:theOption
	set aString to theString
	set x to the offset of theChar in aString
	if (theOption = "begin") then
		return (text from x to 1 of aString)
	else if (theOption = "end") then
		return (text from (x + 2) to -1 of aString)
	else
		error number -128
	end if
end stringOffsetBeginOrEnd:withOffset:withOption:

Interesting and useful, thanks!