Glass Dome: prevent link rot inside markdown notes

Hey there :wave:

Long time no see.

I’ve been busy importing, editing and deleting thousands of old notes. There were a lot of dead links and missing image files.

A couple of months ago, I created a small Alfred workflow that tries to prevent this from happening again. It’s coming in handy right now.

Even if you don’t want to use Alfred (or macOS), maybe you can check the source scripts for inspiration.

Hope this helps (someone else besides me). Cheers!


About Glass Dome

Inspired by Jeff Huang’s manifesto Designed to Last, Glass Dome is an effort to prevent link rot inside markdown notes.

It uses the Internet Archive’s Wayback Machine and your own Dropbox account to preserve three kinds of link:

  • Webpages
  • Inline images
  • File attachments

Setup

:point_right: You need Alfred with its paid Powerpack to use this workflow. For years now, Alfred has been the single most important productivity app on all my macs, so I couldn’t recommend it enough.

To use image and attachment functionality, you’ll also need a (free) Dropbox account. The web archiving feature doesn’t need anything and works out of the box.

Download the workflow file, double-click on it and define the following two variables in the dialog that pops up:

db_token (Dropbox Token)

Fill in “db_token” with your Dropbox authorization token. Go here:

Create an app (name it whatever you like) and generate a token.

db_folder (Dropbox Folder)

This is the folder in which Glass Dome will place your uploaded files and images. If it doesn’t exist, it will be automatically created on first use.

Files are never overwritten: they are timestamped to the second, and the API call is set to auto-rename.

How to use

Each of the three actions is triggered by a keyword (feel free to change those, by the way).

  • gdw (Webpage): It creates Wayback Machine snapshots of any URL passed to it and returns markdown links for both live and saved versions. If no URL is passed, it tries to detect the frontmost Safari tab.

  • gdi (Image): Gets selected image file (in Finder) and copies it to a folder in Dropbox (you have to set in the workflow’s Environment Variables). Then, it returns a markdown image link to that file. You can pass it an optional description that will be used as alt-text.

  • gdf (File): Pretty much the same as the previous one, but for any other file type.

Once the markdown link is in your clipboard, you can paste it anywhere you want.

  • Web links will be appended with a ⟲ icon, which you can click to visit the snapshot
  • Images will render inline (where supported, of course)
  • File links will link to the Dropbox copy of the original local file. (Bonus: those links won’t break even if you move the file to another Dropbox folder.)

A couple of warnings

  1. This is a very basic workflow put together in a day, with very limited programming knowledge. I made it for my own use, but then figured it might be useful to someone else. It’s not elegantly crafted, and it won’t be regularly maintained. It seems to work, though.
  2. I can’t imagine how this workflow could break anything. But I may be wrong.

Comments or suggestions?

Email: [email protected]

Twitter: @senhortavares

3 Likes

Hi!

I’m trying to get your (amazing) Alfred workflow to work, but I’m having no success.

Specifically, no file or image is uploaded to Dropbox, and when invoking the link workflow, it does not result in an internet archive link.

I’m not entirely sure what goes wrong, but for the image and file workflows, there seems to be something going wrong when it executes the dbshare.py:

/Users/sveinung/Library/Application Support/CleanShot/media/media_goP3kT8eDo/CleanShot 2020-07-28 at [email protected]
https://witt-software.com/rockettypist/
[10:46:16.783] Logging Started...
[10:46:32.653] Glass Dome[Keyword] Processing complete
[10:46:32.660] Glass Dome[Keyword] Passing output '' to Arg and Vars
[10:46:32.666] Glass Dome[Arg and Vars] Processing complete
[10:46:32.667] Glass Dome[Arg and Vars] Passing output '' to Run Script
[10:46:32.944] Glass Dome[Run Script] Processing complete
[10:46:32.959] Glass Dome[Run Script] Passing output '/Users/sveinung/Library/Mobile Documents/iCloud~com~sonnysoftware~bot/Documents/Damasio - Self comes to mind.pdf
' to Transform
[10:46:32.960] Glass Dome[Transform] Processing complete
[10:46:32.962] Glass Dome[Transform] Passing output '/Users/sveinung/Library/Mobile Documents/iCloud~com~sonnysoftware~bot/Documents/Damasio - Self comes to mind.pdf' to Arg and Vars
[10:46:32.963] Glass Dome[Arg and Vars] Processing complete
[10:46:32.964] Glass Dome[Arg and Vars] Passing output '/Users/sveinung/Library/Mobile Documents/iCloud~com~sonnysoftware~bot/Documents/Damasio - Self comes to mind.pdf' to Replace
[10:46:32.966] Glass Dome[Replace] Processing complete
[10:46:32.967] Glass Dome[Replace] Passing output 'Damasio - Self comes to mind.pdf' to Arg and Vars
[10:46:32.968] Glass Dome[Arg and Vars] Processing complete
[10:46:32.969] Glass Dome[Arg and Vars] Passing output '"""/Users/sveinung/Library/Mobile Documents/iCloud~com~sonnysoftware~bot/Documents/Damasio - Self comes to mind.pdf""" 4u-iwwVkYEYAAAAAAAAOupiN2FCjnfHCX8FMajAQOgrjAckr1rZCp92GuQhPNq29 """GlassDome"""' to Run Script
[10:46:33.111] STDERR: Glass Dome[Run Script] Traceback (most recent call last):
  File "dbshare2.py", line 4, in <module>
    import sys, os, requests, json, time
ImportError: No module named requests
[10:46:33.127] Glass Dome[Run Script] Processing complete
[10:46:33.142] Glass Dome[Run Script] Passing output '
' to Transform
[10:46:33.144] Glass Dome[Transform] Processing complete
[10:46:33.145] Glass Dome[Transform] Passing output '' to Conditional
[10:46:33.146] Glass Dome[Conditional] Processing complete
[10:46:33.147] Glass Dome[Conditional] Passing output '' to Arg and Vars
[10:46:33.148] Glass Dome[Arg and Vars] Processing complete
[10:46:33.149] Glass Dome[Arg and Vars] Passing output '[Damasio - Self comes to mind.pdf]()' to Copy to Clipboard
[10:46:33.150] Glass Dome[Copy to Clipboard] Processing complete
[10:46:33.151] Glass Dome[Copy to Clipboard] Passing output '[Damasio - Self comes to mind.pdf]()' to Post Notification

And for the webpage archiving:

[10:58:09.460] Glass Dome[Keyword] Processing complete
[10:58:09.486] Glass Dome[Keyword] Passing output 'https://www.nrk.no/vestland/snart-kjem-cruiseskipa-tilbake-_-men-passasjerane-far-ikkje-ga-i-land-1.15102133' to Conditional
[10:58:09.487] Glass Dome[Conditional] Processing complete
[10:58:09.488] Glass Dome[Conditional] Passing output 'https://www.nrk.no/vestland/snart-kjem-cruiseskipa-tilbake-_-men-passasjerane-far-ikkje-ga-i-land-1.15102133' to Arg and Vars
[10:58:09.490] Glass Dome[Arg and Vars] Processing complete
[10:58:09.491] Glass Dome[Arg and Vars] Passing output 'https://www.nrk.no/vestland/snart-kjem-cruiseskipa-tilbake-_-men-passasjerane-far-ikkje-ga-i-land-1.15102133' to Run Script
[10:59:25.605] STDERR: Glass Dome[Run Script] % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed

  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:01 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:02 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:03 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:04 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:05 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:06 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:07 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:08 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:09 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:10 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:11 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:12 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:13 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:14 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:15 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:16 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:17 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:18 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:19 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:20 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:21 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:22 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:23 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:24 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:25 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:26 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:27 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:28 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:29 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:30 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:31 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:32 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:33 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:34 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:35 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:36 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:37 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:38 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:39 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:40 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:42 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:43 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:44 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:45 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:46 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:47 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:48 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:49 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:50 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:51 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:52 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:53 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:54 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:55 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:56 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:57 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:58 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:00:59 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:01:00 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:01:01 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:01:02 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:01:03 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:01:04 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:01:05 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:01:06 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:01:07 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:01:08 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:01:09 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:01:10 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:01:11 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:01:12 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:01:13 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:01:14 --:--:--     0
  0     0    0     0    0     0      0      0 --:--:--  0:01:15 --:--:--     0
100   463  100   463    0     0      6      0  0:01:17  0:01:16  0:00:01    93
100   463  100   463    0     0      6      0  0:01:17  0:01:16  0:00:01   117
[10:59:25.636] Glass Dome[Run Script] Processing complete
[10:59:25.637] Glass Dome[Run Script] Passing output '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to target URL: <a href="/web/20200728085905/https://www.nrk.no/vestland/snart-kjem-cruiseskipa-tilbake-_-men-passasjerane-far-ikkje-ga-i-land-1.15102133">/web/20200728085905/https://www.nrk.no/vestland/snart-kjem-cruiseskipa-tilbake-_-men-passasjerane-far-ikkje-ga-i-land-1.15102133</a>.  If not click the link.' to Copy to Clipboard
[10:59:25.639] Glass Dome[Copy to Clipboard] Processing complete
[10:59:25.640] Glass Dome[Copy to Clipboard] Passing output '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to target URL: <a href="/web/20200728085905/https://www.nrk.no/vestland/snart-kjem-cruiseskipa-tilbake-_-men-passasjerane-far-ikkje-ga-i-land-1.15102133">/web/20200728085905/https://www.nrk.no/vestland/snart-kjem-cruiseskipa-tilbake-_-men-passasjerane-far-ikkje-ga-i-land-1.15102133</a>.  If not click the link.' to Post Notification
1 Like

Hey, @Sveinung. Thanks for bringing this to my attention.

I believe it’s fixed now.

Image/File: There was a missing Python package (which is now included)
Web archive: Link creation no longer waits for cURL to finish.

Please re-download and reinstall the workflow (don’t forget to set your own DB token and base folder) and let me know how it goes.

Hi @macedotavares. Thanks for the quick attention! Unfortunately, it still seems to be broken, this time mentioning, specifically, the module `urllib3:

[15:25:15.915] Logging Started...
[15:25:23.868] Glass Dome[Keyword] Processing complete
[15:25:23.874] Glass Dome[Keyword] Passing output '' to Arg and Vars
[15:25:23.875] Glass Dome[Arg and Vars] Processing complete
[15:25:23.877] Glass Dome[Arg and Vars] Passing output '' to Run Script
[15:25:24.115] Glass Dome[Run Script] Processing complete
[15:25:24.135] Glass Dome[Run Script] Passing output '/Users/sveinung/Documents/Fremtenkt/Verksteder/Pilot, Fyllingsdalen VGS juni 2020/Bilder/Redigerte/Verkstedsbilder/_MG_0957_DxO.jpg
' to Transform
[15:25:24.139] Glass Dome[Transform] Processing complete
[15:25:24.140] Glass Dome[Transform] Passing output '/Users/sveinung/Documents/Fremtenkt/Verksteder/Pilot, Fyllingsdalen VGS juni 2020/Bilder/Redigerte/Verkstedsbilder/_MG_0957_DxO.jpg' to Arg and Vars
[15:25:24.141] Glass Dome[Arg and Vars] Processing complete
[15:25:24.142] Glass Dome[Arg and Vars] Passing output '/Users/sveinung/Documents/Fremtenkt/Verksteder/Pilot, Fyllingsdalen VGS juni 2020/Bilder/Redigerte/Verkstedsbilder/_MG_0957_DxO.jpg' to Replace
[15:25:24.144] Glass Dome[Replace] Processing complete
[15:25:24.145] Glass Dome[Replace] Passing output '_MG_0957_DxO.jpg' to Arg and Vars
[15:25:24.146] Glass Dome[Arg and Vars] Processing complete
[15:25:24.147] Glass Dome[Arg and Vars] Passing output '"""/Users/sveinung/Documents/Fremtenkt/Verksteder/Pilot, Fyllingsdalen VGS juni 2020/Bilder/Redigerte/Verkstedsbilder/_MG_0957_DxO.jpg""" 4u-iwwVkYEYAAAAAAAAOupiN2FCjnfHCX8FMajAQOgrjAckr1rZCp92GuQhPNq29 """GlassDome"""' to Run Script
[15:25:24.261] STDERR: Glass Dome[Run Script] Traceback (most recent call last):
  File "dbshare2.py", line 4, in <module>
    import sys, os, requests, json, time
  File "/Users/sveinung/Library/Application Support/Alfred/Alfred.alfredpreferences/workflows/user.workflow.F960A4EE-52A8-4E12-A9A3-6D4D4CC87AD1/requests/__init__.py", line 43, in <module>
    import urllib3
ImportError: No module named urllib3
[15:25:24.276] Glass Dome[Run Script] Processing complete
[15:25:24.277] Glass Dome[Run Script] Passing output '
' to Transform
[15:25:24.279] Glass Dome[Transform] Processing complete
[15:25:24.280] Glass Dome[Transform] Passing output '' to Conditional
[15:25:24.281] Glass Dome[Conditional] Processing complete
[15:25:24.295] Glass Dome[Conditional] Passing output '' to Arg and Vars
[15:25:24.297] Glass Dome[Arg and Vars] Processing complete
[15:25:24.298] Glass Dome[Arg and Vars] Passing output '![_MG_0957_DxO.jpg]()' to Copy to Clipboard
[15:25:24.299] Glass Dome[Copy to Clipboard] Processing complete
[15:25:24.301] Glass Dome[Copy to Clipboard] Passing output '![_MG_0957_DxO.jpg]()' to Post Notification

Yeah, there were still some dependencies missing.

I updated the GitHub repo. I hope everything’s OK, now.

Let me know if it’s not.

To anyone else reading this: the workflow now works amazingly well, and is tremendously useful if you have images, files or weblinks in your notes.

Thoroughly recommended!

1 Like

:rotating_light::warning: The Wayback Machine is going through some issues, and although the director told me yesterday that everything should be ok now, it apparently isn’t.

So. Beware. For the sake of speed, the workflow does not wait for any server confirmation; the link it generates may actually point to an empty snapshot.

I am currently working on an (additional) alternative that saves a self-contained html file to Dropbox. In the meantime, I don’t know of any possible replacement for the WB, so I guess we’ll just have to wait and hope.

Mark and IA are friends :slight_smile:

I am also keen on saving html bundles!

1 Like

A new version is now on GitHub. It uses Archive.is instead of the IA and copies a bundled html to the dropbox folder. I’ve only tested it on my Macs.

If you give it a spin, let me know how it goes.

Cheers.

Would it be possible to use this with an S3 bucket or something instead of Dropbox?

I’m not familiar with S3, but if it has an API, something that can be called from the command line, it can probably be done.