Does anybody know how to sort Safari History (without opening Safari)…?
I am trying to read the history file but I get much “garbage” of the meaningful kind (but not yet meaningful to me):
set SafHist to alias (((path to home folder) as Unicode text) & "Library:Safari:History.plist") as string
set SafHistText to ReadFile(SafHist)
on ReadFile(SafHist)
set x to ""
try
open for access alias SafHist
set x to read alias SafHist
close access alias SafHist
on error
close access alias SafHist
end try
if x ≠"" then
return x
else
return ""
end if
end ReadFile
If you have Mac OS X 10.4, then System Events can be used to parse the property list file:
Example:
set historyFile to (path to home folder as Unicode text) & "Library:Safari:History.plist"
tell application "System Events"
set historyFileContent to value of property list items of property list item "WebHistoryDates" of property list file historyFile
end tell
The problem is that the file is probably formatted in ‘binary’, as opposed to xml. To get around this the tool plutil can be used (btw System Events can read both formats with ease, but use this method if you can’t use the above):
try
set historyFile to ((path to home folder as Unicode text) & "Library:Safari:History.plist") as alias
on error
display dialog "Safari's history file does not exist!" with icon caution
return
end try
set posixHistoryFile to quoted form of POSIX path of historyFile
do shell script "plutil -convert xml1 " & posixHistoryFile
try
open for access historyFile
set fileContent to read historyFile
close access historyFile
on error
close access historyFile
end try
-- Do something with fileContent
-- the binary format is superior to xml (faster)
-- and is the default in Tiger
do shell script "plutil -convert binary1 " & posixHistoryFile
As for saving changes into the file, you’ll have to get someone else to lend you a hand there (I haven’t really explored into property lists that much), but give me time and I will be happy to give it a go.
That’s exactly what I was looking for.
However, the |lastVisitedDate| in an array of 500 items is in the order of 174884083.4 which can’t be converted into a date string and is difficult to understand.
How to sort/filter those URL’s that were visited in say the last 2 days…?
The value is actually a numeric string ” eg. “174884083.4”. It’ll need confirmation from others, but the formula for turning this into the date of the last visit appears to be:
. the decimal place presumably representing tenths of a second and presumably being rounded off during the addition.
If that’s right, the formula for setting a cutoff “date” of forty-eight hours ago would be:
On my machine, the items produced by Qwerty’s System Events script are listed in date order, the most recent first. So to get the history for the past forty-eight hours:
set historyFile to (path to home folder as Unicode text) & "Library:Safari:History.plist"
set cutoff to (current date) - 2 * days - (date "Monday 1 January 2001 00:00:00") - (time to GMT)
tell application "System Events"
set historyFileContent to value of property list items of property list item "WebHistoryDates" of property list file historyFile
end tell
set recentHistory to historyFileContent
repeat with i from 1 to (count historyFileContent)
if (|lastVisitedDate| of item i of historyFileContent comes before cutoff) then
if (i > 1) then
set recentHistory to items 1 thru (i -1) of historyFileContent
else
set recentHistory to {}
end if
exit repeat
end if
end repeat
recentHistory
But as I said, this needs confirmation from people in other time zones.
I believe you’re right about the time calculation, Nigel.
I was going to say that the alternative approach below should avoid any error caused by an undefined variable, if the most recent date available occurs later than the cut-off. However, I see you’ve now trapped that one, too.
set historyFile to (path to library folder from user domain as Unicode text) & "Safari:History.plist"
set cutoff to (current date) - 2 * days - (date "Monday, January 1, 2001 00:00:00") - (time to GMT) as string
tell application "System Events" to set recentHistory to value of (property list items of property list item "WebHistoryDates" of property list file historyFile whose value of property list item "lastVisitedDate" comes after cutoff)
I tried something like that, but it didn’t work. Obviously I can’t have tried it quite right.
Perhaps it only works as a one-liner.
Edit: Ah. That’s what it was. I didn’t coerce ‘cutoff’ to string and so got an empty list as the result. My thinking was that for a short period in September 2032 (!), ‘cutoff’ will have more digits than earlier ‘lastVisitedDates’. It will begin with “1” and they with “9”, which means that, lexically, they’ll come after it.
But in fact, my script still has that problem. The Unicode text value is mentioned before the number in the comparison, so the number is coerced to Unicode text for the comparison. If the number was mentioned first, the Unicode text would would be coerced to number instead and the comparison would be numerical instead of lexical:
-- 'cutoff' is a number, |lastVisitedDate|s are Unicode texts.
-- Change this .
if (|lastVisitedDate| of item i of historyFileContent comes before cutoff) then
-- . to this:
if (cutoff comes after |lastVisitedDate| of item i of historyFileContent) then
Tiger has a new attribute considering numeric strings that allows numeric texts to be compared numerically instead of lexically. (Both items must be texts.) This might suit kai’s version of the script. I haven’t reset my system date to see if the attribute works with a ‘whose’ filter, but it might by 2032!
set historyFile to (path to library folder from user domain as Unicode text) & "Safari:History.plist"
set cutoff to (current date) - 2 * days - (date "Monday, January 1, 2001 00:00:00") - (time to GMT) as string
considering numeric strings
tell application "System Events" to set recentHistory to value of (property list items of property list item "WebHistoryDates" of property list file historyFile whose value of property list item "lastVisitedDate" comes after cutoff)
end considering