I need to produce a macOS script that retrieves an imported item’s x-devonthink-item:// link given its filesystem path. My search for a solution in the DEVONthink scripting dictionary was fruitless, but I may have looked too cursorily. Is a solution available?
Background: Every book or article in my biblatex (bibliography) database contains the filesystem path of the respective PDF. These paths point to locations within the DEVONthink database “package” and thus only work on macOS—they are entirely meaningless on the iPad. This is why I would like to replace them with x-devonthink-item:// links.
const app = Application("DEVONthink") // use "DEVONthink 3" for DT3
const dbName = "MyDatabase"; // change to your database
const db = app.databases[dbName];
const basePath = `${db.path()}/Files.noindex/`;
function getUUIDfromPath(path) {
const fixedPath = path.startsWith('.') ? basePath + path.replace(/^\.\//,'') : path;
const records = db.contents.whose({path: fixedPath})();
return records && records.length >= 1 ? records[0].uuid() : undefined;
}
const uuid = getUUIDfromPath('./pdf/xx/yyy.pdf');
console.log(uuid);
The important part are the lines beforefunction getUUIDfromPath and this function itself, ending with the }.
The function expects a path of the form ./pdf/xx/yyy.pdf or /Users/home/folder…/MyDatabase.dtBase2/files.noindex/pdf.xx/yyy.pdf, and returns the record’s UUID, if it finds the path. Otherwise, it returns undefined.
You must set the value of databaseName to the name of the database containing your PDFs.
tell application id "DNtp"
if (selected records) is {} then return
set sel to (selected record 1)
set theLink to ((ASCII character 92) & "href{" & reference URL of sel & "}{" & (name without extension of sel) & "}" as string)
set the clipboard to theLink
end tell
--> \href{x-devonthink-item://73847502-CE21-4EF1-A4D9-208645DC9C24}{Screenshot Background Blue}
According to the scripting dictionary, records have a path property:
recordn [inh. item] : A database record
…
path (text) : The POSIX file path of a record. Only the path of external records can be changed.
Here is a third approach:
set thePath to -- However you get the path
tell application id "DNtp"
set DB to current database
set theRecord to first item of (contents of DB whose path = thePath)
set theLink to reference URL of theRecord
end tell
For any future reference, here is a script that performs the BibDesk-DEVONthink automation described above. Thanks to all contributors to this thread for their tips.
In brief, the script accepts selected BibDesk items as input. For every DEVONthink-imported or -indexed attachment in this collection of items, it adds the attachment’s x-devonthink-item URI in a Devonthink field (or DevonthinkN, if N>1) of the respective BibDesk item.
The script enables me to open these attachments directly from my BibLaTeX bibliography manager, both on the Mac (BibDesk) and the iPad (References app).
Any suggestions for improving the script would be warmly welcome. (It is noticeably slow when numerous bibliography items and/or attachments are involved.)
-- Declare global variable for logging
global logFilePath
-- Set log file path
set logFilePath to POSIX path of (path to desktop folder) & "BibDesk-DEVONthink-log.txt"
-- Function to log messages using shell with timestamp
on logMessage(theText)
set timeStamp to do shell script "date '+%Y-%m-%d %H:%M:%S'"
do shell script "echo " & quoted form of ("[" & timeStamp & "] " & theText) & " >> " & quoted form of logFilePath
end logMessage
-- Activate BibDesk
tell application "BibDesk" to activate
delay 0.3
-- Initialize counters
set totalUpdates to 0
set summaryText to ""
-- Access selection from BibDesk
tell application "BibDesk"
if not (exists document 1) then
display dialog "No BibDesk document is open." buttons {"OK"} default button "OK"
return
end if
set theDoc to document 1
try
set selectedPubs to selection of theDoc
if selectedPubs is {} then
display dialog "No references selected in BibDesk." buttons {"OK"} default button "OK"
return
end if
on error
display dialog "Could not retrieve selection. Please make sure the main BibDesk window is frontmost and items are selected." buttons {"OK"} default button "OK"
return
end try
repeat with thisPub in selectedPubs
set pubTitle to title of thisPub
set theAttachments to linked files of thisPub
set addedCount to 0
repeat with thisFile in theAttachments
set filePath to POSIX path of thisFile
set refURL to ""
set foundInDevon to false
with timeout of 600 seconds
tell application id "DNtp"
try
if current database is missing value then
my logMessage("❌ No DEVONthink database is open.")
display dialog "No DEVONthink database is open." buttons {"OK"} default button "OK"
return
end if
set theDatabase to current database
set theMatches to (contents of theDatabase whose path = filePath)
if (count of theMatches) > 0 then
set theRecord to first item of theMatches
set refURL to reference URL of theRecord
set foundInDevon to true
else
my logMessage("NOT FOUND: " & filePath & " (BibDesk: " & pubTitle & ")")
end if
on error errMsg
my logMessage("ERROR: " & errMsg & " for file " & filePath & " (BibDesk: " & pubTitle & ")")
end try
end tell
end timeout
if foundInDevon then
tell thisPub
-- Check if refURL already exists
set refAlreadyExists to false
set devonIndex to 1
repeat while devonIndex ≤ 50
if devonIndex = 1 then
set fieldName to "Devonthink"
else
set fieldName to "Devonthink" & devonIndex
end if
if exists field fieldName then
set existingVal to value of field fieldName
if existingVal is equal to refURL then
set refAlreadyExists to true
exit repeat
end if
else
exit repeat
end if
set devonIndex to devonIndex + 1
end repeat
-- If not already present, add to first empty field
if refAlreadyExists is false then
set devonIndex to 1
repeat while devonIndex ≤ 50
if devonIndex = 1 then
set fieldName to "Devonthink"
else
set fieldName to "Devonthink" & devonIndex
end if
if not (exists field fieldName) then
make new field with properties {name:fieldName}
set value of field fieldName to refURL
set addedCount to addedCount + 1
exit repeat
else
set existingVal to value of field fieldName
if existingVal is missing value or existingVal is "" then
set value of field fieldName to refURL
set addedCount to addedCount + 1
exit repeat
end if
end if
set devonIndex to devonIndex + 1
end repeat
end if
end tell
end if
end repeat
if addedCount > 0 then
set totalUpdates to totalUpdates + addedCount
set summaryText to summaryText & "• “" & pubTitle & "”: added " & addedCount & " link(s)" & linefeed
end if
end repeat
end tell
-- Show summary
if totalUpdates > 0 then
display dialog "✅ Added " & totalUpdates & " DEVONthink link(s)." buttons {"OK"} default button "OK"
else
display dialog "No new DEVONthink links were added." buttons {"OK"} default button "OK"
end if