I'm interested in making a Service (using Automator if possible) that allows me to perform an action on a URL.

If the URL exists as plain text, this works just fine.

However, if the URL is a link in rich text, then my service gets the link's title, rather than it's URL.

How can I obtain the URL when activating a Service on a rich text link?

  • Can we assume that no text is selected besides the URL? – Timothy Mueller-Harder Feb 22 '12 at 14:52
  • 1
    Yes, just the link is selected. For example, when viewing a message in Mail.app, if I right-click on a link, it selects the entire link (and only the link), and the right-click menu shows Services that accept Text as input. – smokris Feb 22 '12 at 18:52
  • The thought just occurred to me: could it be that it's receiving it as a "string" rather than as "text"? This probably isn't what's happening, but I think it's a possibility. – Timothy Mueller-Harder Feb 22 '12 at 20:43
  • @TimothyMueller-Harder: I don't understand — how do "string" and "text" differ? What should I change? – smokris Feb 23 '12 at 1:00
  • Never mind—I was thinking Applescript. Upon further Automator research, there's apparently no difference for what you're doing. :) – Timothy Mueller-Harder Feb 23 '12 at 1:05

You can do it using the app TextSoap. The cleaners "Extract URLs by Replacing" and "Extract URLs by Appending" work with rich text format.

You can also use the following AppleScript:

tell application "textsoap7"
    cleanClipboard with "Extract URLs by Replacing"
end tell

I had the exact same problem when I wanted to be able to launch certain links in my non-default browser, and solutions like LinCastor were not working for me. Here is what I did (after a TON of Googling and hours of running up against brick walls):

Create Service

I created a Service in Automator that receives rich text in any application. Then I created a series of actions (all of which are Run AppleScript) to transform the selected data, little by little, to the desired URL. The following sections illustrate the steps.

To be specific, these are all steps within a single workflow, and all are in the order listed here (each step consumes the result of the step before it).

Edit: For folks unfamiliar with how to use this service, you can click on any link in any application (I was using links in Mail that were rich-text, meaning the text displayed was not the URL), and look for the Services sub-menu in the popup. Choose your service from that list, et voila!

Get the Data

on run {input}
    -- Save off the old clipboard data and capture the current selection in its entirety
    set oldClipboard to the clipboard as record
    tell application "System Events" to keystroke "c" using command down

    set plistData to ""
    set retries to 50

    -- Try to get the pList data from the clipboard (may take a little while to appear)
    repeat while plistData = ""
        set clipboardRecord to the clipboard as record

            set plistData to «class weba» of clipboardRecord

            -- In case you want to use RTF instead...
            --set clipRTF to «class RTF » of clipboardRecord
        on error msg
            set retries to retries - 1

            -- If we're out of retries then bail
            if retries < 0 then
                set the clipboard to oldClipboard
                display dialog ("Failed to get the web data: " & msg) buttons {"OK"} default button 1
                error number -1
            end if

            -- ...else ignore the error and retry after a small delay
            delay 0.1
        end try
    end repeat

    -- Restore the old clipboard data
    set the clipboard to oldClipboard

    -- Set up our intermediate plist file
    set plistFileName to (path to temporary items as text) & "safarilink.plist"
    set plistFRef to (open for access file plistFileName with write permission)

        set eof plistFRef to 0
        write plistData to plistFRef

        close access plistFRef
        --display dialog plistFileName
    on error msg
        display dialog ("PList write error: " & msg) buttons {"OK"} default button 1
        close access plistFRef
        error number -1
    end try

    -- Pass the pList file name to the next step
    return plistFileName
end run

Extract the Link HTML

on run {input, parameters}
    set plistFileName to (input as text)

    -- Set up our intermediate HTML link file
    set linkHtmlFileName to (path to temporary items as text) & "safarilink.html"
    set linkHtmlFRef to (open for access file linkHtmlFileName with write permission)

        tell application "System Events"
            set plist to property list file plistFileName
            set entry to contents of plist

            --display dialog "Name: " & (name of entry)
            --display dialog "Kind: " & (kind of entry)
            --display dialog "Text: " & (text of entry)
            set valueRec to (value of entry as record)
            set webMainResource to webMainResource of valueRec
            set webResourceData to webResourceData of webMainResource
            --display dialog "Resourced"

            set eof linkHtmlFRef to 0
            write webResourceData to linkHtmlFRef

            close access linkHtmlFRef
        end tell
    on error msg
        display dialog ("Link HTML generation error: " & msg) buttons {"OK"} default button 1
        close access linkHtmlFRef
        error number -1
    end try

    -- Pass the HTML link file name to the next step
    return linkHtmlFileName
end run

Load the HTML and Extract the URL

on run {input, parameters}
    set linkHtmlFileName to (input as text)

    --set fileSize to 0 
    --tell application "Finder" to set fileSize to size of file linkHtmlFileName

        set htmlContentParts to read file linkHtmlFileName using delimiter "="
    on error msg
        display dialog ("Link HTML load error: " & msg) buttons {"OK"} default button 1
        close access htmlFRef
        error number -1
    end try

    set hrefIndex to -1

    repeat with index from 1 to count of htmlContentParts
        if item index of htmlContentParts ends with "href" then
            set hrefIndex to index
        end if
    end repeat

    if hrefIndex = -1 then
        display dialog "Selection does not contain a link!" buttons {"OK"} default button 1
        error number -1
    end if

    set linkPart to item (hrefIndex + 1) of htmlContentParts
    set splitParts to split(linkPart, "\"")

    return item 2 of splitParts  --index is base-1
end run

on split(theString, theDelimiter)
    -- Save delimiters to restore old settings
    set oldDelimiters to AppleScript's text item delimiters

    -- Set delimiters to delimiter to be used
    set AppleScript's text item delimiters to theDelimiter

    -- Create the array
    set theArray to every text item of theString

    -- Restore the old setting
    set AppleScript's text item delimiters to oldDelimiters

    -- Return the result
    return theArray
end split

At this point, you have your URL (be sure to convert it explicitly to text, lest you end up with mysterious AppleScript errors. From here, I used it to automatically load the link in Safari.

Use your URL

on run {input, parameters}
    set linkURL to (input as text)

        tell application "Safari"
            if not (exists first window) then
                make new window
                set URL of last tab of first window to linkURL
                set visible of first window to true
                tell first window
                    set newTab to make new tab with properties {URL:linkURL}

                    set visible to true
                    set current tab to newTab
                end tell
            end if

        end tell
    on error msg
        display dialog ("Failed to load URL (" & linkURL & ") in Safari: " & msg) buttons {"OK"} default button 1
        error number -1
    end try
end run

Enjoy, and I hope this helps!

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .