Part of workflow discussed here: Automatically capture and annotate items (to use with Obsidian)
Applescript: Devonthink Helper
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
use script "RegexAndStuffLib" version "1.0.7"
on getRecordType(recordType)
if theRecordType is in {"markdown", "«constant ****mkdn»"} then
return "markdown"
else if theRecordType is in {"bookmark", "«constant ****DTnx»"} then
return "bookmark"
else if theRecordType is in {"pdf", "PDF document", "«constant ****pdf »"} then
return "pdf"
else
return missing value
end if
end getRecordType
on getAncestorTags(theRecord)
tell application id "DNtp"
set tagList to {}
if (exists parent 1 of theRecord) then
return (tags of theRecord) & my getAncestorTags(parent 1 of theRecord)
else
return tags of theRecord
end if
end tell
end getAncestorTags
on getExtension(theFilename)
set theExtension to ""
set {od, AppleScript's text item delimiters} to {AppleScript's text item delimiters, "."}
set theFilenameComponents to text items of theFilename
if (count of theFilenameComponents) is greater than 1 then
set theExtension to "." & last item of theFilenameComponents
end if
set AppleScript's text item delimiters to od
return theExtension
end getExtension
on trimText(theText)
set commandString to "echo " & quoted form of theText & " | sed -E 's|^\\n$||'" as string
set theResult to do shell script commandString without altering line endings
return theResult
end trimText
on formatDate(theDate)
set theFormatter to current application's NSDateFormatter's new()
theFormatter's setDateFormat:"yyyy-MM-dd HH:mm"
return (theFormatter's stringFromDate:(theDate)) as string
end formatDate
(*
on urlencode(str)
return percent encode str encoded characters "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_."
end urlencode
*)
-- Source https://discourse.devontechnologies.com/t/applescript-encoding-urls-and-smart-rules/59555/2
-- Source https://forum.latenightsw.com/t/open-in-script-debugger-links-gone/682/9
on urlencode(str) -- http://harvey.nu/applescript_url_encode_routine.html
local nsStr
set nsStr to current application's NSString's stringWithString:(str)
--set characterSet to current application's NSCharacterSet's URLQueryAllowedCharacterSet()
set characterSet to current application's class "NSCharacterSet"'s characterSetWithCharactersInString:("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.")
return (nsStr's stringByAddingPercentEncodingWithAllowedCharacters:characterSet) as text
end urlencode
on replaceText(find, replace, subject)
set prevTIDs to text item delimiters of AppleScript
set text item delimiters of AppleScript to find
set subject to text items of subject
set text item delimiters of AppleScript to replace
set subject to subject as text
set text item delimiters of AppleScript to prevTIDs
return subject
end replaceText
on sanitize(str)
-- We also exclude # to prevent from unwanted taggingev
set replaceChars to {"%", "&", "/", "?", "<", ">", "\\", "*", "|", ":", "*"}
set stripChars to {"#"}
set theResult to ""
repeat with char in str
if char is in replaceChars then
set char to "-"
else if char is in stripChars then
set char to ""
end if
set theResult to theResult & char
end repeat
if theResult ends with "." then set theResult to characters 1 thru -2 of theResult as string
return theResult
end sanitize
on simpleSort(my_list)
set the index_list to {}
set the sorted_list to {}
repeat (the number of items in my_list) times
set the low_item to ""
repeat with i from 1 to (number of items in my_list)
if i is not in the index_list then
set this_item to item i of my_list as text
if the low_item is "" then
set the low_item to this_item
set the low_item_index to i
else if this_item comes before the low_item then
set the low_item to this_item
set the low_item_index to i
end if
end if
end repeat
set the end of sorted_list to the low_item
set the end of the index_list to the low_item_index
end repeat
return the sorted_list
end simpleSort
on uniqueList(theList)
set resultList to {}
repeat with i from 1 to count of items of theList
if item i of theList is not in resultList then
set end of resultList to item i of theList
end if
end repeat
return resultList
end uniqueList
Smart rule script: content tagged or moved
- Search Content folder
- Kind is Any Document
- On Moving, On Classifying, On Tagging, On Demand
use DT : script "DEVONthink helper"
use ma : script "Markdown Annotation helper"
use script "RegexAndStuffLib" version "1.0.7"
on performSmartRule(theRecords)
tell application id "DNtp"
repeat with theRecord in theRecords
set theTags to tags of theRecord
set thePath to path of theRecord
if exists annotation of theRecord then
set maRecord to annotation of theRecord
set maText to plain text of maRecord
set maText to ma's updateTags(maText, theTags, false, true)
set maText to ma's updatePath(maText, thePath, false)
set plain text of maRecord to maText
end if
end repeat
end tell
end performSmartRule
Smart rule script: (Re)process content
- Search Content folder
- Kind is Markdown, Bookmark, PDF/PS
- On Import, On Demand
- See Applescript: Process incoming annotation (in this post)
Smart rule script: Content renamed
- Kind is Any Document
- On Renaming
use DT : script "DEVONthink helper"
use ma : script "Markdown Annotation helper"
on performSmartRule(theRecords)
tell application id "DNtp"
repeat with theRecord in theRecords
set theRecordName to name of theRecord
set theRecordExtension to DT's getExtension(theRecordName)
set maTitle to DT's sanitize(theRecordName)
set name of theRecord to maTitle & theRecordExtension
set maPath to path of theRecord
if exists annotation of theRecord then
set maRecord to annotation of theRecord
set name of maRecord to maTitle & ".md"
set maText to plain text of maRecord
set maText to ma's updateTitle(maText, maTitle, maPath, false)
set plain text of maRecord to maText
end if
end repeat
end tell
end performSmartRule
Smart rule script: Content tagged or removed
- Search Content folder
- Kind is Any Document
- On Moving, On Classifying, On Tagging, On Demand
use DT : script "DEVONthink helper"
use ma : script "Markdown Annotation helper"
use script "RegexAndStuffLib" version "1.0.7"
on performSmartRule(theRecords)
tell application id "DNtp"
repeat with theRecord in theRecords
set theTags to tags of theRecord
set thePath to path of theRecord
if exists annotation of theRecord then
set maRecord to annotation of theRecord
set maText to plain text of maRecord
set maText to ma's updateTags(maText, theTags, false, true)
set maText to ma's updatePath(maText, thePath, false)
set plain text of maRecord to maText
end if
end repeat
end tell
end performSmartRule
Smart rule script: annotation renamed
- Search Annotation folder
- Kind is Markdown
- On Renaming
use ma : script "Markdown Annotation helper"
use DT : script "DEVONthink helper"
use script "RegexAndStuffLib" version "1.0.7"
on performSmartRule(theRecords)
tell application id "DNtp"
repeat with theRecord in theRecords
set theRecordName to name of theRecord
set maText to plain text of theRecord
set maTitle to DT's sanitize(theRecordName)
set itemUUID to ma's getItemUUID(maText)
if itemUUID is not equal to "" then
set itemRecord to get record with uuid itemUUID
set itemRecordExtension to DT's getExtension(name of itemRecord)
set name of itemRecord to maTitle & itemRecordExtension
set maPath to path of itemRecord
end if
set maText to ma's updateTitle(maText, maTitle, maPath, false)
set plain text of theRecord to maText
set name of theRecord to maTitle & ".md"
end repeat
end tell
end performSmartRule
Smart rule script: annotation updated
- Search Annotation folder
- Kind is Markdown
- After Saving
use ma : script "Markdown Annotation helper"
use DT : script "DEVONthink helper"
use script "RegexAndStuffLib" version "1.0.7"
on performSmartRule(theRecords)
tell application id "DNtp"
repeat with theRecord in theRecords
set theRecordName to name of theRecord
set theRecordExtension to DT's getExtension(theRecordName)
set maText to plain text of theRecord
set maPath to path of theRecord
set maExcerpt to ma's getExcerpt(maText)
set maURL to ma's getURL(maText)
set maTags to ma's getTags(maText)
-- reference: on updateText(maText, theDate, theTitle, theURL, thePDFURL, theAnnotationURL, theTags, theExcerpt, stripTagsFromContent)
set itemUUID to ma's getItemUUID(maText)
if itemUUID is not equal to "" then
set itemRecord to get record with uuid itemUUID
if maExcerpt is not equal to "" then
set comment of itemRecord to maExcerpt
end if
if maTags is not equal to {} then
set tags of itemRecord to maTags
end if
end if
set maText to ma's updateText(maText, missing value, missing value, maExcerpt, maURL, missing value, missing value, maPath, maTags, true)
set plain text of theRecord to maText
end repeat
end tell
end performSmartRule
Smart rule script: set original tags
- Search Capture folder (before tagging / classifying)
- Kind is Any Document
- On Creation, On Import, On Tagging
use script "RegexAndStuffLib" version "1.0.7"
use scripting additions
on performSmartRule(theRecords)
tell application id "DNtp"
repeat with theRecord in theRecords
set theTagsList to tags of theRecord
set cleanedTagsList to {}
repeat with theTag in theTagsList
if (theTag as string) is not equal to "00 processed" then
set the end of cleanedTagsList to theTag as string
end if
end repeat
set cleanedTagsString to join strings cleanedTagsList using delimiter ","
add custom meta data cleanedTagsString for "originaltags" to theRecord
end repeat
end tell
end performSmartRule
Smart rule script: apply original tags
- Search Content folder
- Kind is Any Document
- On Classifying, On Demand
- After Execute Script also: Apply Rule "On replicating / tagging"
use AppleScript version "2.4" -- Yosemite (10.10) or later
use script "RegexAndStuffLib" version "1.0.7"
use scripting additions
on performSmartRule(theRecords)
tell application id "DNtp"
repeat with theRecord in theRecords
set recordTags to tags of theRecord
set originalTags to get custom meta data for "originaltags" from theRecord
if originalTags is not missing value then
set originalTagsList to split string originalTags using delimiters ","
set tags of theRecord to (recordTags & originalTagsList)
end if
end repeat
end tell
end performSmartRule
Javascript for use with Markdown documents
- The Keyboard Maestro in this script is a simple KM macro that opens the supplied link - this is to prevent the restriction in Obsidian to open
file://
links (and it also prevents that DT links to the item in DT if it recognizes the file:// URL for an item in the database; I want to specifically open the item in the original app / Finder)
document.addEventListener('DOMContentLoaded', () => {
var title = document.querySelector('title').innerText;
var date = document.querySelector('meta[name="date"]');
var url = document.querySelector('meta[name="url"]');
var itemurl = document.querySelector('meta[name="itemurl"]');
var annotationurl = document.querySelector('meta[name="annotationurl"]');
var path = document.querySelector('meta[name="path"]');
var tags = document.querySelector('meta[name="tags"]');
if(date) { date = date.content }
if(url) { url = url.content }
if(itemurl) { itemurl = itemurl.content }
if(annotationurl) { annotationurl = annotationurl.content }
if(tags) { tags = tags.content.substring(1, tags.content.length - 1) }
if(path) { path = path.content }
body = document.querySelector("body").innerHTML;
info = "<p><a href=\"" + itemurl + "\">Item</a> | <a href=\"obsidian://open?vault=Notes&file="+ encodeURIComponent(title) + "\">Obsidian</a> | <a href=\"kmtrigger://macro=Open&value=%2FUsers%2Fmdbraber%2FLibrary%2FMobile%20Documents%2Fcom~apple~CloudDocs%2F"+ encodeURIComponent(path) + "\">File</a><br/>Tags: <i>"+ tags +"</i></p><h1>" + title + "</h1>";
document.querySelector("body").innerHTML = info + "\n" + body;
})