<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title>Just a Summary</title><link href=""/><updated>2025-10-21T17:39:00Z</updated><id/><author><name>Piers Cawley</name></author><generator uri="https://gohugo.io/" version="0.154.5">Hugo</generator><entry><title>Restaurant Review: Slice</title><link href="https://bofh.org.uk/2025/10/21/restaurant-review-slice/"/><id>https://bofh.org.uk/2025/10/21/restaurant-review-slice/</id><updated>2025-10-21T17:39:00Z</updated><summary>&lt;p&gt;The last time I was in New York
&lt;object role="note"&gt;&lt;p&gt;Which may well turn out to be my Last Time in New York, for both environmental and political reasons.&lt;/p&gt;</summary><content type="html">&lt;p&gt;The last time I was in New York
&lt;object role="note"&gt;&lt;p&gt;Which may well turn out to be my Last Time in New York, for both environmental and political reasons.&lt;/p&gt;
&lt;/object&gt;
I was on my way to OSCON in Portland, OR but I took the opportunity to spend a couple of days with my much missed friend David (dha) Adler &amp;ndash; founder of the New York Perl Mongers, Doctor Who fan, terrible movie aficionado, comics fan, fountain pen collector and all around delightful nerd.&lt;/p&gt;
&lt;p&gt;For lunch that first day, he took me to his favourite New York restaurant, Shopsin&amp;rsquo;s in a corner of Essex Market. The menu was bewilderingly long and seemed to make absolutely no sense, but once I&amp;rsquo;d finally decided (almost by shutting my eyes and pointing at random) what I was having, the food was magnificent. Flavour combinations of sweet and savoury that I would never have thought of. Ludicrously large portions. The very best kind of brusque service. And a queue halfway around the market by the time we&amp;rsquo;d finished to let us know exactly how special the place was. We ate lunch there every day of my layover and everything I was similarly bonkers and delicious. The pastrami on rye from Katz&amp;rsquo;s deli that evening was pretty damned overwhelming too, but that was a one and done. Shopsin&amp;rsquo;s was a place I could have happily eaten my lunch at for the rest of time.&lt;/p&gt;
&lt;p&gt;Which brings me to &lt;a href="https://sliceofcornwall.com/"&gt;Slice&lt;/a&gt;, which sits in an unassuming shed on a back road halfway between Gweek and Falmouth in Cornwall. Another Perl monger friend recommended it to us as a great place for brunch, so we paid a6 visit when we were on holiday a couple of weeks back.&lt;/p&gt;
&lt;p&gt;As soon as I saw the menu&amp;mdash;stupidly long, full of sweet/savoury flavour collisions, remarkably inexpensive&amp;mdash;I thought, &amp;ldquo;Hmm&amp;hellip; this reminds me of somewhere.&amp;rdquo; I ordered a &amp;ldquo;Morio II&amp;rdquo;, Slice&amp;rsquo;s take on a steak sandwich made on a sourdough flatbread packed with steak, caramelized onions, cheese, hash browns and really good salad greens, and the first bite was a seriously emotional experience. It was nothing like anything I ate in New York, but I was back there in the corner booth by the pass, talking shite with a much-missed friend and love, love &lt;em&gt;loving&lt;/em&gt; the food. Everything about that meal was about as close to perfect as you can get. The Morio II was amazing. Every bite a brilliant collision of deliciousness. Gill&amp;rsquo;s &amp;ldquo;Full Hash Up&amp;rdquo; was a more conventional take on the full English breakfast
&lt;object role="note"&gt;&lt;p&gt;she swapped the tomatoes and beans for spiced house rosti at the suggestion of our server, Lily. A very good call indeed. Thanks Lily.&lt;/p&gt;
&lt;/object&gt;
which I confess I helped her to finish and which was very good indeed.
And, oh god, the huge charred marshmallow and hot chocolate I had to finish up? Stunning.&lt;/p&gt;
&lt;p&gt;We didn&amp;rsquo;t manage to eat there every day after that, but we did get back once and the food was just as good and imaginative. My &amp;ldquo;The King&amp;rdquo;&amp;mdash;waffles with steak, maple syrup, butter and deep fried crispy poached eggs&amp;mdash;was good, but not to my taste.
&lt;object role="note"&gt;&lt;p&gt;Except for those deep fried crispy poached eggs! Wow!&lt;/p&gt;
&lt;/object&gt;
Gill&amp;rsquo;s poached eggs on sourdough toast with green oil was everything you could want, and was quickly polished off without my assistance (sadly).&lt;/p&gt;
&lt;p&gt;Both delightful experiences, with that lovely Cornish casual service that&amp;rsquo;s commonplace on the peninsula: Friendly, passionate about the food and helpful. As typically Cornish as Luke&amp;rsquo;s service at Shopsin&amp;rsquo;s was stereotypical New York service.&lt;/p&gt;
&lt;p&gt;I really can&amp;rsquo;t recommend the place highly enough. If you have fond memories of Shopsin&amp;rsquo;s and can get to the middle of nowhere in Cornwall, then you should &lt;em&gt;definitely&lt;/em&gt; give this place a try. And if you have no idea what I&amp;rsquo;m talking about, but you like generous plates of enormously tasty surprising combinations of food, then get your arse down there too. You won&amp;rsquo;t regret it.&lt;/p&gt;
&lt;p&gt;And if you have the time, go and sit on the harbour wall in Porthleven with an Ann&amp;rsquo;s
&lt;object role="note"&gt;&lt;p&gt;Not Philps’, good and close as they are&lt;/p&gt;
&lt;/object&gt;
Pasty and admire the fine selection of gorgeous dogs taking the air with their owners while you eat. It&amp;rsquo;s a great tradition of ours that more people should share.&lt;/p&gt;
</content></entry><entry><title>Talking to the Wayback Machine</title><link href="https://bofh.org.uk/2025/10/02/talking-to-the-wayback-machine/"/><id>https://bofh.org.uk/2025/10/02/talking-to-the-wayback-machine/</id><updated>2025-10-02T14:14:00Z</updated><summary>&lt;p&gt;Way back in 2016, I &lt;a href="https://bofh.org.uk/2016/06/19/static-migration/"&gt;migrated this site&lt;/a&gt; from its &lt;a href="https://publify.github.io/"&gt;Publify&lt;/a&gt;
&lt;object role="note"&gt;&lt;p&gt;Publify is the Rails based blogging engine that started out as Typo, which I ended up maintaining for a while before handing it off to &lt;a href="http://t37.net/"&gt;Frédéric de Villamil&lt;/a&gt;, who is still on the current maintenance team. Go him!&lt;/p&gt;</summary><content type="html">&lt;p&gt;Way back in 2016, I &lt;a href="https://bofh.org.uk/2016/06/19/static-migration/"&gt;migrated this site&lt;/a&gt; from its &lt;a href="https://publify.github.io/"&gt;Publify&lt;/a&gt;
&lt;object role="note"&gt;&lt;p&gt;Publify is the Rails based blogging engine that started out as Typo, which I ended up maintaining for a while before handing it off to &lt;a href="http://t37.net/"&gt;Frédéric de Villamil&lt;/a&gt;, who is still on the current maintenance team. Go him!&lt;/p&gt;
&lt;/object&gt;
incarnation to a static site generated from markdown files by &lt;a href="https://gohugo.io"&gt;Hugo&lt;/a&gt;. In that migration, I fucked up and truncated a buttload of posts and didn&amp;rsquo;t realise what I&amp;rsquo;d done until long after (about a week ago now) I had misplaced the database that the site had originally been generated from.&lt;/p&gt;
&lt;p&gt;&lt;span class="newthought"&gt;Oops.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;However, the Internet is still a marvellous place, blessed with useful sites like &lt;a href="https://web.archive.org/"&gt;The Wayback Machine&lt;/a&gt;, which lets the interested reader browse historic versions of web pages. Which means, provided a page got noticed by archive.org&amp;rsquo;s crawlers, I can fetch a page from back before I fucked up and, with a little bit of massaging, turn it into something that Hugo can understand and get the whole article back again.
&lt;object role="note"&gt;&lt;p&gt;I can even recover the comments, which I had deliberately left out of the initial import, thinking “I’ll get around to importing those as well one day!” Thanks to my ADHD, that never quite happened. Unless you count the current activity, of course.&lt;/p&gt;
&lt;/object&gt;&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Provided&amp;rdquo; is doing a lot of work there, and some posts definitely got missed, but something is better that nothing.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve reached the point in my recovery process that I&amp;rsquo;ve started to hate the ad hoc way I was grabbing stuff from the archive. I want to only grab an archived page if the archived version is from before I buggered things up. So I&amp;rsquo;ve written some Emacs lisp. Obviously.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what I&amp;rsquo;d like to write.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-emacs-lisp" data-lang="emacs-lisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;with-wayback-page-from-before&lt;/span&gt; &lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="mi"&gt;20160618&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;web-tidy-buffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;fixup-escaped-typo:code-blocks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;convert-to-org-mode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;restrict-to-article-and-comments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;fixup-comments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;org-string-nw-p&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;buffer-substring-no-properties&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;point-min&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;point-max&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The idea being that I fetch the archived version of the post into a temporary buffer where I can run it through &lt;a href="https://www.html-tidy.org/"&gt;HTML Tidy&lt;/a&gt; and a few extr HTML cleanup steps
&lt;object role="note"&gt;&lt;p&gt;An ever expanding list of cleanup steps. Every time I have to tidy something up by hand for the second time, I add something to the cleanup pipeline.&lt;/p&gt;
&lt;/object&gt;
before converting it to Org format with &lt;a href="https://pandoc.org/"&gt;Pandoc&lt;/a&gt;, continuing the cleanup in org mode (I prefer Emacs org mode tooling to its HTML tooling) and returning a nice clean string to insert into the org capture buffer.&lt;/p&gt;
&lt;p&gt;&lt;span class="newthought"&gt;I plan to return to the cleanup in future articles,&lt;/span&gt;
but we&amp;rsquo;re just concerned with &lt;code&gt;with-wayback-page-from-before&lt;/code&gt; for now.
&lt;object role="note"&gt;&lt;p&gt;If you want to see the full code (along with way more stuff), you’ll find it in my &lt;a href="https://github.com/pdcawley/dotemacs?tab=readme-ov-file#using-the-wayback-machine"&gt;dotemacs&lt;/a&gt; repo on Github. I’m exceedingly unlikely to turn this into a full &lt;code&gt;wayback.el&lt;/code&gt; package, but you’re more than welcome to use it as a starting point.&lt;/p&gt;
&lt;/object&gt;&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what that looks like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-emacs-lisp" data-lang="emacs-lisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defmacro&lt;/span&gt; &lt;span class="nv"&gt;with-wayback-page-from-before&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="nv"&gt;date&lt;/span&gt; &lt;span class="kp"&gt;&amp;amp;rest&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;declare&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;indent&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;debug&lt;/span&gt; &lt;span class="no"&gt;t&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;capture-url&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;make-symbol&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;capture-url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;when-let*&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;capture-url&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;wayback-get-capture-before&lt;/span&gt; &lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;with-temp-buffer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;request&lt;/span&gt; &lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;capture-url&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;:sync&lt;/span&gt; &lt;span class="no"&gt;t&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;:success&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cl-function&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;&amp;amp;key&lt;/span&gt; &lt;span class="nv"&gt;data&lt;/span&gt; &lt;span class="kp"&gt;&amp;amp;allow-other-keys&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt; &lt;span class="nv"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;,@&lt;/span&gt;&lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It&amp;rsquo;s a macro that uses &lt;code&gt;wayback-get-capture-before&lt;/code&gt; to find the URL of the most recent capture of our target &lt;code&gt;url&lt;/code&gt; before the given &lt;code&gt;date&lt;/code&gt;, fetches it into a temporary buffer and executes the body of the macro. A common Emacs pattern.
&lt;object role="note"&gt;&lt;p&gt;do &lt;code&gt;M-x describe-function&lt;/code&gt; and type &lt;code&gt;with-&lt;/code&gt; and check out the completions to see just how common&lt;/p&gt;
&lt;/object&gt;
The real meat lies in &lt;code&gt;wayback-get-capture-before&lt;/code&gt;, which uses the &lt;a href="https://github.com/internetarchive/wayback/blob/master/wayback-cdx-server/README.md"&gt;Wayback CDX Server API&lt;/a&gt; to discover the capture url we&amp;rsquo;re interested in. There are other, simpler to use Wayback machine APIs, but they only let us find the closest capture to our date of interest, and we want to find the most recent capture that&amp;rsquo;s strictly before our date and that requires the CDX API. I&amp;rsquo;ve been a little lazy and used the &lt;a href="https://github.com/tkf/emacs-request"&gt;&lt;strong&gt;request&lt;/strong&gt;&lt;/a&gt; package to do the web request stuff because I prefer its API to the native &lt;code&gt;url-retrieve&lt;/code&gt; in vanilla Emacs.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-emacs-lisp" data-lang="emacs-lisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defvar&lt;/span&gt; &lt;span class="nv"&gt;wayback-cdx-endpoint&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;https://web.archive.org/cdx/search/cdx&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;The endpoint for the Wayback Machine&amp;#39;s CDX server.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defvar&lt;/span&gt; &lt;span class="nv"&gt;wayback-cdx-json-parser&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;apply-partially&lt;/span&gt; &lt;span class="ss"&gt;&amp;#39;json-parse-buffer&lt;/span&gt; &lt;span class="nb"&gt;:array-type&lt;/span&gt; &lt;span class="ss"&gt;&amp;#39;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;Parser for json data returned from the CDX server.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;wayback-get-capture-before&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="nv"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;Use the CDX applet to find any version of URL captured before DATE string.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;Returns nil if there&amp;#39;s no such capture&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;capture-url&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;request&lt;/span&gt; &lt;span class="nv"&gt;wayback-cdx-endpoint&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;:params&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;to&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;numberp&lt;/span&gt; &lt;span class="nv"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;stringp&lt;/span&gt; &lt;span class="nv"&gt;date&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;date&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;format-time-string&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;%Y%m%d%H%M%S&amp;#34;&lt;/span&gt; &lt;span class="nv"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;collapse&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;output&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;fl&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;timestamp,original&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="mi"&gt;-1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;:parser&lt;/span&gt; &lt;span class="nv"&gt;wayback-cdx-json-parser&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;:sync&lt;/span&gt; &lt;span class="no"&gt;t&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;:success&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cl-function&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;&amp;amp;key&lt;/span&gt; &lt;span class="nv"&gt;data&lt;/span&gt; &lt;span class="kp"&gt;&amp;amp;allow-other-keys&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;setq&lt;/span&gt; &lt;span class="nv"&gt;capture-url&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;pcase&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;cadr&lt;/span&gt; &lt;span class="nv"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;target-url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;s-lex-format&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;https://web.archive.org/web/${timestamp}/${target-url}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)))))))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;capture-url&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The CDX API&amp;rsquo;s JSON response format is derived from a CSV style text file. We&amp;rsquo;re only really interested in the timestamp and the &amp;ldquo;original&amp;rdquo; url that our target url resolved to, so we set &lt;code&gt;(fl . &amp;quot;timestamp,original&amp;quot;)&lt;/code&gt; in the request parameters and limit the results to the most recent one (&lt;code&gt;(limit . -1)&lt;/code&gt;) before (&lt;code&gt;(to . ...)&lt;/code&gt;) the date we&amp;rsquo;re interested in. That gives us:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;timestamp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;original&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;20250212175727&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://bofh.org.uk/2016/06/19/static-migration/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can tell it comes from something CSV like, can&amp;rsquo;t you?&lt;/p&gt;
&lt;p&gt;The JSON response gets parsed into a Lisp list and we extract the interesting bits using a &lt;code&gt;pcase&lt;/code&gt; statement&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-emacs-lisp" data-lang="emacs-lisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;pcase&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;cadr&lt;/span&gt; &lt;span class="nv"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;target-url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;s-lex-format&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;which passes an empty list through, or grabs the &lt;code&gt;timestamp&lt;/code&gt; and &lt;code&gt;target-url&lt;/code&gt; from the second entry in the results list and uses &lt;code&gt;s-lex-format&lt;/code&gt; to generate a wayback machine URL. Easy.&lt;/p&gt;
&lt;p&gt;&lt;span class="newthought"&gt;This has the makings of a more general package,&lt;/span&gt;
but that&amp;rsquo;s very much a back burner project. It does what I need, and does it well enough that I can consider this yak shaved and get on with the job of recovering my truncated blog posts. I&amp;rsquo;ll continue on my way of not releasing anything that anyone might want me to support.&lt;/p&gt;
</content></entry><entry><title>Making use of Webmentions</title><link href="https://bofh.org.uk/2025/09/09/making-use-of-webmentions/"/><id>https://bofh.org.uk/2025/09/09/making-use-of-webmentions/</id><updated>2025-09-09T20:49:00Z</updated><summary>&lt;p&gt;When &lt;a href="/2025/09/07/fetching-webmentions-again-with-emacs/"&gt;last we left off&lt;/a&gt; we had worked out how to grab all the mentions of this site that &lt;a href="https://webmentions.io/"&gt;Webmentions.io&lt;/a&gt; knew about and now we want to write that out to the &lt;code&gt;data/&lt;/code&gt; directory in a format that&amp;rsquo;s easy to deal with in Hugo.&lt;/p&gt;</summary><content type="html">&lt;p&gt;When &lt;a href="/2025/09/07/fetching-webmentions-again-with-emacs/"&gt;last we left off&lt;/a&gt; we had worked out how to grab all the mentions of this site that &lt;a href="https://webmentions.io/"&gt;Webmentions.io&lt;/a&gt; knew about and now we want to write that out to the &lt;code&gt;data/&lt;/code&gt; directory in a format that&amp;rsquo;s easy to deal with in Hugo.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve read &lt;a href="/note/10/"&gt;my earlier note&lt;/a&gt;, you&amp;rsquo;ll know that I&amp;rsquo;ve been evolving the data schema towards something that&amp;rsquo;s easy for Hugo to deal with and reasonably comprehensible for me too.&lt;/p&gt;
&lt;p&gt;As things currently stand, I&amp;rsquo;ve settled on dropping all the mentions in a single file, &lt;code&gt;data/mentions/all.json&lt;/code&gt;
&lt;object role="note"&gt;&lt;p&gt;I’d rather use &lt;code&gt;data/mentions.json&lt;/code&gt;, but Hugo’s data system doesn’t seem to pick that up, so I’ll live with the slightly more clunky option.&lt;/p&gt;
&lt;/object&gt;
which is structured along these lines:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;/note/7/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;like-of&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;entry&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;author&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;card&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Daniel Kelly Music&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;photo&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://avatars.webmention.io/fsn1.your-objectstorage.com/7aa4815cbc1f993c0e2a6df03280dc168f5ad07fecd0313ddfc27eeb02e0b437.png&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://aus.social/@yasslad&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://mendeddrum.org/@pdcawley/115160370354067277#favorited-by-109307830089461078&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;published&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-received&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2025-09-07T01:31:46Z&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1936817&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-source&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://brid.gy/like/mastodon/@pdcawley@mendeddrum.org/115160370354067277/109307830089461078&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-target&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://bofh.org.uk/note/7/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-protocol&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;webmention&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;like-of&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://bofh.org.uk/note/7/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-property&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;like-of&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-private&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;entry&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;author&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;card&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Jess Robinson&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;photo&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://avatars.webmention.io/fsn1.your-objectstorage.com/a11ee0a58e54873140bf1f1965900378d866fce3fafae79e116b979bea3a8773.jpg&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://fosstodon.org/@castaway&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://mendeddrum.org/@pdcawley/115160370354067277#favorited-by-109562941096076318&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;published&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-received&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2025-09-07T07:19:01Z&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1936873&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-source&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://brid.gy/like/mastodon/@pdcawley@mendeddrum.org/115160370354067277/109562941096076318&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-target&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://bofh.org.uk/note/7/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-protocol&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;webmention&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;like-of&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://bofh.org.uk/note/7/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-property&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;like-of&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-private&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;in-reply-to&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;mention-of&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;repost-of&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;other&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Just imagine that JSON object has a bunch more paths as keys referencing similar objects keyed by mention type.&lt;/p&gt;
&lt;p&gt;As it stands, &lt;code&gt;wm--fetch-all&lt;/code&gt; is returning a flat sequence of webmention objects that we want to process into a more structured object,
&lt;object role="note"&gt;&lt;p&gt;JSON/Javascript calls them objects, old Perl heads like me think of them as hashes, and they’re “Hash Tables” in Emacs Lisp. I’ll be calling them hashes from now on.&lt;/p&gt;
&lt;/object&gt;
in other words we want to fold (or &amp;ldquo;reduce&amp;rdquo; in Emacs Lisp terminology) the sequence into a hash. And I know just the function for that. Let&amp;rsquo;s see what &lt;code&gt;describe-function&lt;/code&gt; has to say about &lt;code&gt;seq-reduce&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;seq-reduce is a byte-compiled function defined in seq.el.gz.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;*Signature*
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;(seq-reduce FUNCTION SEQUENCE INITIAL-VALUE)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;*Documentation*
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Reduce the function FUNCTION across SEQUENCE, starting
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;with INITIAL-VALUE.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Return the result of calling FUNCTION with INITIAL-VALUE
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;and the first element of SEQUENCE, then calling FUNCTION
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;with that result and the second element of SEQUENCE,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;then with that result and the third element of SEQUENCE,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;etc. FUNCTION will be called with INITIAL-VALUE (and then
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;the accumulated value) as the first argument, and the
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;elements from SEQUENCE as the second argument.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;If SEQUENCE is empty, return INITIAL-VALUE and FUNCTION
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;is not called.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;This does not modify SEQUENCE.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So we can write&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-emacs-lisp" data-lang="emacs-lisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;seq-reduce&lt;/span&gt; &lt;span class="nf"&gt;#&amp;#39;&lt;/span&gt;&lt;span class="nv"&gt;wm--add-mention-to-hash-table&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;wm--fetch-all&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;make-hash-table&lt;/span&gt; &lt;span class="nb"&gt;:test&lt;/span&gt; &lt;span class="ss"&gt;&amp;#39;equal&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and that will handle the business of iterating over the sequence of mentions for us, and all we have to do is write &lt;code&gt;wm--add-mention-to-hash-table&lt;/code&gt; to populate the hash we made with &lt;code&gt;(make-hash-table :test 'equal)&lt;/code&gt;
&lt;object role="note"&gt;&lt;p&gt;We need to use that &lt;code&gt;:test &amp;rsquo;equal&lt;/code&gt; part because &lt;code&gt;json-insert&lt;/code&gt; wants a hash with strings as keys and the default hash returned by &lt;code&gt;(make-hash-table)&lt;/code&gt; compares keys using &lt;code&gt;eql&lt;/code&gt; which might or might not work when comparing strings. Not a problem which &lt;code&gt;equal&lt;/code&gt; has.&lt;/p&gt;
&lt;/object&gt;
one mention at a time, and return the modified hash (You and I both know that it&amp;rsquo;s the same old hash mutated, but let&amp;rsquo;s pretend it isn&amp;rsquo;t, eh?).&lt;/p&gt;
&lt;p&gt;What does that function look like? Here&amp;rsquo;s what I wrote:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-emacs-lisp" data-lang="emacs-lisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;wm--add-mention-to-hash-table&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;acc&lt;/span&gt; &lt;span class="nv"&gt;mention&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;Helps reduce a list of mentions into a two level hash.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="ss"&gt;&amp;#39;dash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;let*&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;path&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;--&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;mention&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;gethash&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;wm-target&amp;#34;&lt;/span&gt; &lt;span class="nv"&gt;it&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;url-generic-parse-url&lt;/span&gt; &lt;span class="nv"&gt;it&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;url-path-and-query&lt;/span&gt; &lt;span class="nv"&gt;it&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;car&lt;/span&gt; &lt;span class="nv"&gt;it&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;mentions-hash&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;gethash&lt;/span&gt; &lt;span class="nv"&gt;path&lt;/span&gt; &lt;span class="nv"&gt;acc&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;wm-new-mentions-hash&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;mention-type&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;gethash&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;wm-property&amp;#34;&lt;/span&gt; &lt;span class="nv"&gt;mention&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;mentions&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;gethash&lt;/span&gt; &lt;span class="nv"&gt;mention-type&lt;/span&gt; &lt;span class="nv"&gt;mentions-hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;progn&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;setq&lt;/span&gt; &lt;span class="nv"&gt;mention-type&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;other&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;gethash&lt;/span&gt; &lt;span class="nv"&gt;mention-type&lt;/span&gt; &lt;span class="nv"&gt;mentions-hash&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;new-mentions&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;seq-contains&lt;/span&gt; &lt;span class="nv"&gt;mentions&lt;/span&gt; &lt;span class="nv"&gt;mention&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;mentions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;vconcat&lt;/span&gt; &lt;span class="nv"&gt;mentions&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt; &lt;span class="nv"&gt;mention&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;puthash&lt;/span&gt; &lt;span class="nv"&gt;mention-type&lt;/span&gt; &lt;span class="nv"&gt;new-mentions&lt;/span&gt; &lt;span class="nv"&gt;mentions-hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;puthash&lt;/span&gt; &lt;span class="nv"&gt;path&lt;/span&gt; &lt;span class="nv"&gt;mentions-hash&lt;/span&gt; &lt;span class="nv"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We grab the path from the &lt;code&gt;&amp;quot;wm-target&amp;quot;&lt;/code&gt; key, which is actually a URL rather than a simple path
&lt;object role="note"&gt;&lt;p&gt;We &lt;em&gt;could&lt;/em&gt; just use the URL, and that would work fine on this site, but not when I’m running on localhost. The path will always match with &lt;code&gt;.RelPermalink&lt;/code&gt;, but the host part of &lt;code&gt;.Permalink&lt;/code&gt; is different in development than in production.&lt;/p&gt;
&lt;/object&gt;
so, rather than writing&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-emacs-lisp" data-lang="emacs-lisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;car&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;url-path-and-query&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;url-generic-parse-url&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;gethash&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;wm-target&amp;#34;&lt;/span&gt; &lt;span class="nv"&gt;mention&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;we&amp;rsquo;ll thread &lt;code&gt;mention&lt;/code&gt; through that series of transformations using &lt;code&gt;dash.el&lt;/code&gt;&amp;rsquo;s threading macro, &lt;code&gt;--&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We use the path to grab &lt;code&gt;mentions-hash&lt;/code&gt; from the &lt;code&gt;acc&lt;/code&gt;-umulating hash and, if there isn&amp;rsquo;t already one there, we grab an empty, but structured hash using &lt;code&gt;wm-new-mentions-hash&lt;/code&gt;, which looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-emacs-lisp" data-lang="emacs-lisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;wm-new-mentions-hash&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;Make a new empty hash to hold categorised webmention data.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;copy-hash-table&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nv"&gt;s&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;hash-table&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;test&lt;/span&gt; &lt;span class="nf"&gt;equal&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;data&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;like-of&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;in-reply-to&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;mention-of&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;repost-of&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;other&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;[]))))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we look up &lt;code&gt;&amp;quot;wm-property&amp;quot;&lt;/code&gt; in &lt;code&gt;mention&lt;/code&gt;, and use that to grab its associated vector of mentions. Well, we would, but there&amp;rsquo;s a small wrinkle.&lt;/p&gt;
&lt;p&gt;&lt;span class="newthought"&gt;We’re only currently interested in four kinds of mention,&lt;/span&gt;
but Webmention.io doesn&amp;rsquo;t know that. We could throw the extras away, but what if we became interested in &lt;code&gt;bookmark-of&lt;/code&gt; mentions or whatever somewhere down the road. So let&amp;rsquo;s collect them under the &lt;code&gt;other&lt;/code&gt; key. Which is where this hacky section of our &lt;code&gt;let*&lt;/code&gt; form comes in:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-emacs-lisp" data-lang="emacs-lisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;mention-type&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;gethash&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;wm-property&amp;#34;&lt;/span&gt; &lt;span class="nv"&gt;mention&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;mentions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;gethash&lt;/span&gt; &lt;span class="nv"&gt;mention-type&lt;/span&gt; &lt;span class="nv"&gt;mentions-hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;progn&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;setq&lt;/span&gt; &lt;span class="nv"&gt;mention-type&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;other&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;gethash&lt;/span&gt; &lt;span class="nv"&gt;mention-type&lt;/span&gt; &lt;span class="nv"&gt;mentions-hash&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What&amp;rsquo;s going on here then?&lt;/p&gt;
&lt;p&gt;First, we make a guess at the &lt;code&gt;mention-type&lt;/code&gt; we&amp;rsquo;re going to file the current mention under by grabbing the &lt;code&gt;&amp;quot;wm-property&amp;quot;&lt;/code&gt; and use that value to lookup the mention type in &lt;code&gt;mentions-hash&lt;/code&gt;. If it&amp;rsquo;s one of the four types we&amp;rsquo;re interested in, that will be a vector, which is truth-y, otherwise we get &lt;code&gt;nil&lt;/code&gt;, which is false-y so we change the mention type to &amp;ldquo;other&amp;rdquo; and grab that vector from the mention hash.&lt;/p&gt;
&lt;p&gt;We now know the key path we&amp;rsquo;re going to store our mentions in, and we have the current vector of mentions associated with it. So, if we already know &lt;code&gt;(seq-contains mentions mention)&lt;/code&gt; about the current mention, we reuse that, otherwise we make a new vector with the current mention added to it.&lt;/p&gt;
&lt;p&gt;That done, it&amp;rsquo;s a simple matter of putting the new mentions vector into our mentions hash, putting the mentions hash into our accumulating hash and returning that.&lt;/p&gt;
&lt;p&gt;With that done, it&amp;rsquo;s a simple matter of opening &lt;code&gt;data/mentions/all.json&lt;/code&gt;, erasing the buffer, calling &lt;code&gt;(json-insert (seq-reduce ...))&lt;/code&gt; to update the data and saving it. Here&amp;rsquo;s the code which does exactly that.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-emacs-lisp" data-lang="emacs-lisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;wm-unflatten-mentions&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;mentions-vec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;seq-reduce&lt;/span&gt; &lt;span class="ss"&gt;&amp;#39;wm--add-mention-to-hash-table&lt;/span&gt; &lt;span class="nv"&gt;mentions-vec&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;make-hash-table&lt;/span&gt; &lt;span class="nb"&gt;:test&lt;/span&gt; &lt;span class="ss"&gt;&amp;#39;equal&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;wm-fetch-mentions&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;Fetch the webmentions of &lt;/span&gt;&lt;span class="ss"&gt;`wm-domain&amp;#39;&lt;/span&gt;&lt;span class="s"&gt;.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;interactive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;save-current-buffer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;all-entries&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;wm--fetch-all&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;with-temp-file&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;expand-file-name&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;all.json&amp;#34;&lt;/span&gt; &lt;span class="nv"&gt;wm-data-dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;erase-buffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;json-insert&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;wm-unflatten-mentions&lt;/span&gt; &lt;span class="nv"&gt;all-entries&lt;/span&gt;&lt;span class="p"&gt;))))))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;span class="newthought"&gt;Over in the Hugo partial&lt;/span&gt;
that renders the bit of the page immediately after this, we can get at the data like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go-html-template" data-lang="go-html-template"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;{{-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$all_mentions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;site&lt;/span&gt;&lt;span class="na"&gt;.Data.mentions.all&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;.RelPermalink&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;-}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$likes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$all_mentions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;like-of&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;slice&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;-}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$reposts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$all_mentions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;repost-of&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;slice&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;-}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$replies&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$all_mentions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;in-reply-to&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;slice&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;-}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$mentions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$all_mentions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;mention-of&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;slice&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;-}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;footer&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;metaline&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;response-summary&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$likes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;len&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt; &lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;len&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$likes&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;like&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;likes&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$reposts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;len&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt; &lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;len&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$reposts&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;repost&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;reposts&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$replies&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;len&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt; &lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;len&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$replies&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;reply&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;replies&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$mentions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;len&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt; &lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;len&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$mentions&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;mention&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;mentions&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;footer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I&amp;rsquo;ll leave the rest as an exercise for the interested reader. However, I will note that the Webmention.io API includes the option to pass in a &lt;code&gt;since&lt;/code&gt; argument, so it wouldn&amp;rsquo;t be hard to write&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-emacs-lisp" data-lang="emacs-lisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;seq-reduce&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="ss"&gt;&amp;#39;wm--add-mention-to-hash-table&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;wm-fetch-mentions-since&lt;/span&gt; &lt;span class="nv"&gt;wm-last-checked&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;wm-parse-mentions-file&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;expand-file-name&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;mentions/all.json&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;wm-data-dir&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;without having to change our reducing function at all.&lt;/p&gt;
&lt;p&gt;Separation of concerns, baby! Separation of concerns!&lt;/p&gt;
</content></entry><entry><title>Fetching webmentions again. With Emacs this time!</title><link href="https://bofh.org.uk/2025/09/07/fetching-webmentions-again-with-emacs/"/><id>https://bofh.org.uk/2025/09/07/fetching-webmentions-again-with-emacs/</id><updated>2025-09-07T17:17:00Z</updated><summary>&lt;p&gt;You might have noticed, if you&amp;rsquo;re a regular visitor that webmentions have started showing up on the site again. I turned them off a while ago, &lt;object role="note"&gt;&lt;p&gt;I turned off the home server that was handling the web hook calls from Webmention.io, planning to quickly move it and spin it up again. Ask me how that’s going.&lt;/p&gt;</summary><content type="html">&lt;p&gt;You might have noticed, if you&amp;rsquo;re a regular visitor that webmentions have started showing up on the site again. I turned them off a while ago, &lt;object role="note"&gt;&lt;p&gt;I turned off the home server that was handling the web hook calls from Webmention.io, planning to quickly move it and spin it up again. Ask me how that’s going.&lt;/p&gt;
&lt;/object&gt;
but &lt;a href="https://aaronparecki.com"&gt;Aaron Parecki&lt;/a&gt;&amp;rsquo;s invaluable &lt;a href="https://webmention.io"&gt;Webmention.io&lt;/a&gt; service has still been gathering them for me, so I&amp;rsquo;ve turned them back on. But in the mean time, I mislaid the code I was using to populate the necessary Hugo data files from Webmention. Exploratory code ahoy.&lt;/p&gt;
&lt;h2 id="start-by-faking-it"&gt;Start by faking it&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;m heavily indebted to &lt;a href="https://masto.hackers.town/@randomgeek"&gt;Brian Wisti&lt;/a&gt; for his post, &lt;a href="https://randomgeekery.org/post/2020/11/using-the-webmention.io-api/"&gt;Using the Webmention.io API&lt;/a&gt; as the starting point to my explorations, but since I can&amp;rsquo;t be doing with Python, I used emacs.&lt;/p&gt;
&lt;p&gt;I started with my very minor fork of &lt;a href="https://github.com/pdcawley/restclient.el"&gt;&lt;code&gt;restclient&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;
&lt;p&gt;&lt;a id="code-snippet--last5"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-restclient" data-lang="restclient"&gt;# Grab the most recent 5 webmentions of bofh.org.uk
GET https://webmention.io/api/mentions.jf2?domain=bofh.org.uk&amp;amp;sort-dir=down&amp;amp;per-page=5&amp;amp;token=:token
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Which produces the following JSON data:&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Disclose this for the full wall of JSON
&lt;p&gt;Don&amp;rsquo;t say you weren&amp;rsquo;t warned!&lt;/summary&gt;&lt;/p&gt;
&lt;div class="details"&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;feed&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Webmentions&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;children&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;entry&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;author&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;card&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Mike Spencer&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;photo&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://avatars.webmention.io/fsn1.your-objectstorage.com/3ba5a0fdc660c44995fef428a601770fd0fe2619fc1f8c7e70ec7e9e1da66d4b.jpg&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://mastodon.scot/@mikerspencer&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://mendeddrum.org/@pdcawley/115162152891409560#favorited-by-109365771998686190&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;published&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-received&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2025-09-07T09:35:22Z&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1936897&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-source&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://brid.gy/like/mastodon/@pdcawley@mendeddrum.org/115162152891409560/109365771998686190&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-target&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://bofh.org.uk/note/8/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-protocol&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;webmention&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;like-of&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://bofh.org.uk/note/8/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-property&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;like-of&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-private&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;entry&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;author&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;card&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Jess Robinson&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;photo&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://avatars.webmention.io/fsn1.your-objectstorage.com/a11ee0a58e54873140bf1f1965900378d866fce3fafae79e116b979bea3a8773.jpg&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://fosstodon.org/@castaway&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://mendeddrum.org/@pdcawley/115160370354067277#favorited-by-109562941096076318&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;published&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-received&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2025-09-07T07:19:01Z&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1936873&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-source&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://brid.gy/like/mastodon/@pdcawley@mendeddrum.org/115160370354067277/109562941096076318&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-target&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://bofh.org.uk/note/7/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-protocol&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;webmention&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;like-of&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://bofh.org.uk/note/7/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-property&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;like-of&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-private&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;entry&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;author&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;card&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Daniel Kelly Music&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;photo&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://avatars.webmention.io/fsn1.your-objectstorage.com/7aa4815cbc1f993c0e2a6df03280dc168f5ad07fecd0313ddfc27eeb02e0b437.png&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://aus.social/@yasslad&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://mendeddrum.org/@pdcawley/115160370354067277#favorited-by-109307830089461078&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;published&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-received&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2025-09-07T01:31:46Z&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1936817&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-source&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://brid.gy/like/mastodon/@pdcawley@mendeddrum.org/115160370354067277/109307830089461078&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-target&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://bofh.org.uk/note/7/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-protocol&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;webmention&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;like-of&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://bofh.org.uk/note/7/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-property&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;like-of&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-private&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;entry&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;author&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;card&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Nick Anderson&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;photo&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://avatars.webmention.io/fsn1.your-objectstorage.com/856b210b0770a292a4f9e76699c84aa3833f9252716ad3cab58b50a7b57205ee.jpg&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://fosstodon.org/@nickanderson&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://mendeddrum.org/@pdcawley/115107421781297473#favorited-by-109475479621313511&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;published&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-received&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2025-08-28T21:48:52Z&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1934308&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-source&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://brid.gy/like/mastodon/@pdcawley@mendeddrum.org/115107421781297473/109475479621313511&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-target&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://bofh.org.uk/note/6/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-protocol&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;webmention&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;like-of&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://bofh.org.uk/note/6/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-property&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;like-of&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-private&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;entry&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;author&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;card&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Nick Anderson&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;photo&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://avatars.webmention.io/fsn1.your-objectstorage.com/856b210b0770a292a4f9e76699c84aa3833f9252716ad3cab58b50a7b57205ee.jpg&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://fosstodon.org/@nickanderson&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://fosstodon.org/@nickanderson/115108589417986943&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;published&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2025-08-28T21:48:05+00:00&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-received&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2025-08-28T21:48:51Z&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1934307&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-source&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://brid.gy/comment/mastodon/@pdcawley@mendeddrum.org/115107421781297473/115108589456284711&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-target&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://bofh.org.uk/note/6/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-protocol&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;webmention&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;html&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;p&amp;gt;&amp;lt;span class=\&amp;#34;h-card\&amp;#34;&amp;gt;&amp;lt;a href=\&amp;#34;https://mendeddrum.org/@pdcawley\&amp;#34; class=\&amp;#34;u-url\&amp;#34;&amp;gt;@&amp;lt;span&amp;gt;pdcawley&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/span&amp;gt; I was gonna say, that looks like a source block within a source block which means there&amp;#39;s another src block to make it render. Nice&amp;lt;/p&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;text&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;@pdcawley I was gonna say, that looks like a source block within a source block which means there&amp;#39;s another src block to make it render. Nice&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;in-reply-to&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://bofh.org.uk/note/6/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-property&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;in-reply-to&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-private&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/details&gt;
&lt;p&gt;The essential shape is something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;feed&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Webmentions&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;children&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;wm-target&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://bofh.org.uk/note/8/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-property&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;like-of&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;...&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;wm-target&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://bofh.org.uk/note/8/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wm-property&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;in-reply-to&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;...&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;...&lt;/span&gt; &lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Restclient is great for interactively exploring a RESTful API, but it&amp;rsquo;s not so great for slicing and dicing the data in a Emacs-y way. I could sit down and learn &lt;code&gt;jq&lt;/code&gt; again, but I know Lisp, dammit, so after a frustrating hour or so trying to wrap my head around the default &lt;code&gt;url-retrieve&lt;/code&gt; interfaces, I went and grabbed the &lt;a href="https://github.com/tkf/emacs-request"&gt;&lt;code&gt;emacs-request&lt;/code&gt;&lt;/a&gt; package instead because I found its API more comprehensible.&lt;/p&gt;
&lt;p&gt;Because &lt;code&gt;request&lt;/code&gt; is a function where &lt;code&gt;restclient&lt;/code&gt; is more like an application running in Emacs, it&amp;rsquo;s way more useful for automating things. Here&amp;rsquo;s more or less the same request as above done in Lisp.&lt;/p&gt;
&lt;p&gt;&lt;a id="code-snippet--request-mentions"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-emacs-lisp" data-lang="emacs-lisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;request&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;https://webmention.io/api/mentions.jf2&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;:params&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;domain&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;bofh.org.uk&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;token&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;wm-api-token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;per-page&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;5&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;sort-dir&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;down&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;:parser&lt;/span&gt; &lt;span class="ss"&gt;&amp;#39;json-parse-buffer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;:sync&lt;/span&gt; &lt;span class="no"&gt;t&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;:success&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cl-function&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;&amp;amp;key&lt;/span&gt; &lt;span class="nv"&gt;data&lt;/span&gt; &lt;span class="kp"&gt;&amp;amp;allow-other-keys&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;setq&lt;/span&gt; &lt;span class="nv"&gt;response&lt;/span&gt; &lt;span class="nv"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The code is obviously fiddlier, but it&amp;rsquo;s also programmable and, because we set an arbitrary &lt;code&gt;:parser&lt;/code&gt; function, it&amp;rsquo;s trivial to convert the returned JSON into a native Emacs lisp hash table
&lt;object role="note"&gt;&lt;p&gt;It’s not hard to generate an old school alist either, but that was annoyingly hard to serialise back to JSON, so I went with the default types because they seem to just work.&lt;/p&gt;
&lt;/object&gt;
which looks a bit like this:&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;A wall of Lisp&lt;/summary&gt;
&lt;div class="details"&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;#s(hash-table test equal data
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; (&amp;#34;type&amp;#34; &amp;#34;feed&amp;#34; &amp;#34;name&amp;#34; &amp;#34;Webmentions&amp;#34; &amp;#34;children&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; [#s(hash-table test equal data
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; (&amp;#34;type&amp;#34; &amp;#34;entry&amp;#34; &amp;#34;author&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; #s(hash-table test equal data
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; (&amp;#34;type&amp;#34; &amp;#34;card&amp;#34; &amp;#34;name&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;Mike Spencer&amp;#34; &amp;#34;photo&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;https://avatars.webmention.io/fsn1.your-objectstorage.com/3ba5a0fdc660c44995fef428a601770fd0fe2619fc1f8c7e70ec7e9e1da66d4b.jpg&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;url&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;https://mastodon.scot/@mikerspencer&amp;#34;))
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;url&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;https://mendeddrum.org/@pdcawley/115162152891409560#favorited-by-109365771998686190&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;published&amp;#34; :null &amp;#34;wm-received&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;2025-09-07T09:35:22Z&amp;#34; &amp;#34;wm-id&amp;#34; 1936897
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;wm-source&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;https://brid.gy/like/mastodon/@pdcawley@mendeddrum.org/115162152891409560/109365771998686190&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;wm-target&amp;#34; &amp;#34;https://bofh.org.uk/note/8/&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;wm-protocol&amp;#34; &amp;#34;webmention&amp;#34; &amp;#34;like-of&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;https://bofh.org.uk/note/8/&amp;#34; &amp;#34;wm-property&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;like-of&amp;#34; &amp;#34;wm-private&amp;#34; :false))
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; #s(hash-table test equal data
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; (&amp;#34;type&amp;#34; &amp;#34;entry&amp;#34; &amp;#34;author&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; #s(hash-table test equal data
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; (&amp;#34;type&amp;#34; &amp;#34;card&amp;#34; &amp;#34;name&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;Jess Robinson&amp;#34; &amp;#34;photo&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;https://avatars.webmention.io/fsn1.your-objectstorage.com/a11ee0a58e54873140bf1f1965900378d866fce3fafae79e116b979bea3a8773.jpg&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;url&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;https://fosstodon.org/@castaway&amp;#34;))
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;url&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;https://mendeddrum.org/@pdcawley/115160370354067277#favorited-by-109562941096076318&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;published&amp;#34; :null &amp;#34;wm-received&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;2025-09-07T07:19:01Z&amp;#34; &amp;#34;wm-id&amp;#34; 1936873
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;wm-source&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;https://brid.gy/like/mastodon/@pdcawley@mendeddrum.org/115160370354067277/109562941096076318&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;wm-target&amp;#34; &amp;#34;https://bofh.org.uk/note/7/&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;wm-protocol&amp;#34; &amp;#34;webmention&amp;#34; &amp;#34;like-of&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;https://bofh.org.uk/note/7/&amp;#34; &amp;#34;wm-property&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;like-of&amp;#34; &amp;#34;wm-private&amp;#34; :false))
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; #s(hash-table test equal data
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; (&amp;#34;type&amp;#34; &amp;#34;entry&amp;#34; &amp;#34;author&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; #s(hash-table test equal data
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; (&amp;#34;type&amp;#34; &amp;#34;card&amp;#34; &amp;#34;name&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;Daniel Kelly Music&amp;#34; &amp;#34;photo&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;https://avatars.webmention.io/fsn1.your-objectstorage.com/7aa4815cbc1f993c0e2a6df03280dc168f5ad07fecd0313ddfc27eeb02e0b437.png&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;url&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;https://aus.social/@yasslad&amp;#34;))
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;url&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;https://mendeddrum.org/@pdcawley/115160370354067277#favorited-by-109307830089461078&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;published&amp;#34; :null &amp;#34;wm-received&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;2025-09-07T01:31:46Z&amp;#34; &amp;#34;wm-id&amp;#34; 1936817
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;wm-source&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;https://brid.gy/like/mastodon/@pdcawley@mendeddrum.org/115160370354067277/109307830089461078&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;wm-target&amp;#34; &amp;#34;https://bofh.org.uk/note/7/&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;wm-protocol&amp;#34; &amp;#34;webmention&amp;#34; &amp;#34;like-of&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;https://bofh.org.uk/note/7/&amp;#34; &amp;#34;wm-property&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;like-of&amp;#34; &amp;#34;wm-private&amp;#34; :false))
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; #s(hash-table test equal data
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; (&amp;#34;type&amp;#34; &amp;#34;entry&amp;#34; &amp;#34;author&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; #s(hash-table test equal data
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; (&amp;#34;type&amp;#34; &amp;#34;card&amp;#34; &amp;#34;name&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;Nick Anderson&amp;#34; &amp;#34;photo&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;https://avatars.webmention.io/fsn1.your-objectstorage.com/856b210b0770a292a4f9e76699c84aa3833f9252716ad3cab58b50a7b57205ee.jpg&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;url&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;https://fosstodon.org/@nickanderson&amp;#34;))
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;url&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;https://mendeddrum.org/@pdcawley/115107421781297473#favorited-by-109475479621313511&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;published&amp;#34; :null &amp;#34;wm-received&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;2025-08-28T21:48:52Z&amp;#34; &amp;#34;wm-id&amp;#34; 1934308
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;wm-source&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;https://brid.gy/like/mastodon/@pdcawley@mendeddrum.org/115107421781297473/109475479621313511&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;wm-target&amp;#34; &amp;#34;https://bofh.org.uk/note/6/&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;wm-protocol&amp;#34; &amp;#34;webmention&amp;#34; &amp;#34;like-of&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;https://bofh.org.uk/note/6/&amp;#34; &amp;#34;wm-property&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;like-of&amp;#34; &amp;#34;wm-private&amp;#34; :false))
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; #s(hash-table test equal data
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; (&amp;#34;type&amp;#34; &amp;#34;entry&amp;#34; &amp;#34;author&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; #s(hash-table test equal data
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; (&amp;#34;type&amp;#34; &amp;#34;card&amp;#34; &amp;#34;name&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;Nick Anderson&amp;#34; &amp;#34;photo&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;https://avatars.webmention.io/fsn1.your-objectstorage.com/856b210b0770a292a4f9e76699c84aa3833f9252716ad3cab58b50a7b57205ee.jpg&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;url&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;https://fosstodon.org/@nickanderson&amp;#34;))
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;url&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;https://fosstodon.org/@nickanderson/115108589417986943&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;published&amp;#34; &amp;#34;2025-08-28T21:48:05+00:00&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;wm-received&amp;#34; &amp;#34;2025-08-28T21:48:51Z&amp;#34; &amp;#34;wm-id&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 1934307 &amp;#34;wm-source&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;https://brid.gy/comment/mastodon/@pdcawley@mendeddrum.org/115107421781297473/115108589456284711&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;wm-target&amp;#34; &amp;#34;https://bofh.org.uk/note/6/&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;wm-protocol&amp;#34; &amp;#34;webmention&amp;#34; &amp;#34;content&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; #s(hash-table test equal data
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; (&amp;#34;html&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;&amp;lt;p&amp;gt;&amp;lt;span class=\&amp;#34;h-card\&amp;#34;&amp;gt;&amp;lt;a href=\&amp;#34;https://mendeddrum.org/@pdcawley\&amp;#34; class=\&amp;#34;u-url\&amp;#34;&amp;gt;@&amp;lt;span&amp;gt;pdcawley&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/span&amp;gt; I was gonna say, that looks like a source block within a source block which means there&amp;#39;s another src block to make it render. Nice&amp;lt;/p&amp;gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;text&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;@pdcawley I was gonna say, that looks like a source block within a source block which means there&amp;#39;s another src block to make it render. Nice&amp;#34;))
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;in-reply-to&amp;#34; &amp;#34;https://bofh.org.uk/note/6/&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;wm-property&amp;#34; &amp;#34;in-reply-to&amp;#34; &amp;#34;wm-private&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; :false))]))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/details&gt;
&lt;p&gt;Verbose as hell, but something we can work with. Here&amp;rsquo;s a simplified alist representation which might be a little easier to understand.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-emacs-lisp" data-lang="emacs-lisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;type&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;feed&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Webmentions&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;children&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="p"&gt;[((&lt;/span&gt;&lt;span class="nv"&gt;wm-property&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;like-of&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;wm-target&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;https://bofh.org.uk/note/8/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;wm-property&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;in-reply-to&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;wm-target&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;https://bofh.org.uk/note/8/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)]))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The interesting stuff lives under the &lt;code&gt;&amp;quot;children&amp;quot;&lt;/code&gt; key, which we can get with &lt;code&gt;(gethash &amp;quot;children&amp;quot; data)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To get all the webmentions for our domain, the Webmention API allows for pagination. We can ask for pages of, say 100 entries and if we get 100 entries back, append the result to our running collection of entries and request the next page. Once we get a result with fewer than 100 entries, we know we&amp;rsquo;re done and we can massage the data into a shape that Hugo can cope with&lt;/p&gt;
&lt;h2 id="then-turn-what-we-learn-into-a-commands"&gt;Then turn what we learn into a commands&lt;/h2&gt;
&lt;p&gt;Now we know what the data coming from Webmention.io looks like, and how we can page through it, let&amp;rsquo;s write a function, &lt;code&gt;wm--fetch-all&lt;/code&gt; to do that for us.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;(while more? ...)&lt;/code&gt; loop keeps requesting more data until it gets a short response, at which point &lt;code&gt;more?&lt;/code&gt; becomes false and we return the accumulated &lt;code&gt;all-entries&lt;/code&gt; vector&lt;/p&gt;
&lt;h2 id="and-relax"&gt;And relax&lt;/h2&gt;
&lt;p&gt;We now have a handy list of all the webmentions relating to our site. The next step is to massage it into a data structure that will suit Hugo and export it as JSON files in the site&amp;rsquo;s &lt;code&gt;data&lt;/code&gt; directory. Which is a topic for another blog post, I think. If only because I&amp;rsquo;m reasonably sure that the data structure I&amp;rsquo;m currently using isn&amp;rsquo;t great.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll hack it about a bit and report back.&lt;/p&gt;
</content></entry><entry><title>Fiddling with structural templates in Org-mode</title><link href="https://bofh.org.uk/2025/08/27/fiddling-with-structural-templates-in-org-mode/"/><id>https://bofh.org.uk/2025/08/27/fiddling-with-structural-templates-in-org-mode/</id><updated>2025-08-27T21:59:00Z</updated><summary>&lt;p&gt;I&amp;rsquo;ve been dealing with some enduring niggles in my literate Emacs configuration, and have just landed &lt;a href="https://github.com/pdcawley/dotemacs/commit/dbc316bfca27a37319b5316ea1281ccbebd526aa"&gt;Commit dbc316b&lt;/a&gt;, and I thought I&amp;rsquo;d write about it here because it&amp;rsquo;s an example of what keeps me on Emacs more than thirty five years after I first started using it.&lt;/p&gt;</summary><content type="html">&lt;p&gt;I&amp;rsquo;ve been dealing with some enduring niggles in my literate Emacs configuration, and have just landed &lt;a href="https://github.com/pdcawley/dotemacs/commit/dbc316bfca27a37319b5316ea1281ccbebd526aa"&gt;Commit dbc316b&lt;/a&gt;, and I thought I&amp;rsquo;d write about it here because it&amp;rsquo;s an example of what keeps me on Emacs more than thirty five years after I first started using it.&lt;/p&gt;
&lt;p&gt;Back when I started, Emacs was merely a text editor that felt easier to use than &lt;code&gt;vi&lt;/code&gt;, but these days, it&amp;rsquo;s all that and more. I don&amp;rsquo;t live in Emacs quite as much as I used to; I don&amp;rsquo;t hang on USENET or IRC any more, and I check my email as little as possible, so I&amp;rsquo;ve not got around to configuring Emacs for that again.
&lt;object role="note"&gt;&lt;p&gt;Back in the day, it all went through the behemoth that is GNUS, but I lost that version of my &lt;code&gt;.emacs&lt;/code&gt; file several computers and ISPs ago.&lt;/p&gt;
&lt;/object&gt;
These days, what I like about Emacs is it&amp;rsquo;s malleability. It&amp;rsquo;s not uniquely malleable, either. Vim and Neovim diehards will no doubt have tales to tell about their setups, but Emacs is my editor. There are others like it, but this is mine.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been seduced by the Literate Programming idea of maintaining my &lt;a href="https://github.com/pdcawley/dotemacs"&gt;&lt;code&gt;~/.emacs&lt;/code&gt;&lt;/a&gt;
&lt;object role="note"&gt;&lt;p&gt;Emacs used to keep its config in a file in your home directory called &lt;code&gt;.emacs&lt;/code&gt;, then it moved to &lt;code&gt;~/.emacs.d/init.el&lt;/code&gt;, which made it a little easier to break a big config out into multiple files. These days, Emacs looks first in &lt;code&gt;${XDG_CONFIG_HOME}/emacs&lt;/code&gt;, which defaults to &lt;code&gt;~/.config/emacs&lt;/code&gt; and that’s where my config files live today.&lt;/p&gt;
&lt;/object&gt;
file in a single &lt;code&gt;org-mode&lt;/code&gt; file, which gets &amp;rsquo;tangled&amp;rsquo; into &lt;code&gt;early-init.el&lt;/code&gt; and &lt;code&gt;init.el&lt;/code&gt;, which are the files that Emacs actually loads.&lt;/p&gt;
&lt;p&gt;One feature of Org mode&amp;rsquo;s literate programming support that I rather like and take advantage of is the ability to write things out of order and assemble them correctly using &lt;code&gt;noweb&lt;/code&gt;. The idea is that &lt;code&gt;src&lt;/code&gt; blocks can be named, and then referenced from other source blocks. For instance, you might have:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;#+begin_src emacs-lisp
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-emacs-lisp" data-lang="emacs-lisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;dolist&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;template&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;t&amp;#34;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Task with annotation&amp;#34;&lt;/span&gt; &lt;span class="nv"&gt;entry&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;file&lt;/span&gt; &lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;pdc/org-inbox-file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;* %?\n:PROPERTIES:\n:created: %U\n:END:\n\n%i\n\n~ %a&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;:prepend&lt;/span&gt; &lt;span class="no"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;add-to-list&lt;/span&gt; &lt;span class="ss"&gt;&amp;#39;org-capture-templates&lt;/span&gt; &lt;span class="nv"&gt;template&lt;/span&gt; &lt;span class="no"&gt;t&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt; &lt;span class="nv"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;car&lt;/span&gt; &lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;car&lt;/span&gt; &lt;span class="nv"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;#+end_src
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;which will eventually be tangled as a &lt;code&gt;dolist&lt;/code&gt; that adds &lt;em&gt;all&lt;/em&gt; your capture templates to &lt;code&gt;org-capture-templates&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That &lt;code&gt;&amp;lt;&amp;lt;capture-templates&amp;gt;&amp;gt;&lt;/code&gt; is what makes the magic happen. When we tangle &lt;code&gt;README.org&lt;/code&gt;, the exporter gathers up the contents of any source blocks associated with the tag and replaces &lt;code&gt;&amp;lt;&amp;lt;capture-templates&amp;gt;&amp;gt;&lt;/code&gt; with them
&lt;object role="note"&gt;&lt;p&gt;It applies some heuristics as well, which is why that &lt;code&gt;dolist&lt;/code&gt; isn’t indented exactly how it would be without the noweb reference, but you can read the docs to find out more.&lt;/p&gt;
&lt;/object&gt;
So, elsewhere in my &lt;code&gt;README.org&lt;/code&gt;, I can keep capture templates related to a particular app close to the rest of the configuration of that app, where it makes sense to me. For example, in the section where I configure my blogging tools, I do this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;#+begin_src emacs-lisp :tangle nil :noweb-ref capture-templates
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-emacs-lisp" data-lang="emacs-lisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;b&amp;#34;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;bofh.org.uk post&amp;#34;&lt;/span&gt; &lt;span class="nv"&gt;entry&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;file+headline&lt;/span&gt; &lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;pdc-site-posts-file&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;bofh&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Posts&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;function&lt;/span&gt; &lt;span class="nv"&gt;+org-hugo-new-subtree-post-capture-template&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;#+end_src
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Notice the header arguments. &lt;code&gt;:tangle nil&lt;/code&gt; tells the exporter not to simply write the code out at the current position, and &lt;code&gt;:noweb-ref capture-templates&lt;/code&gt; tells the exporter to instead write it out wherever it sees &lt;code&gt;&amp;lt;&amp;lt;capture-templates&amp;gt;&amp;gt;&lt;/code&gt; in a source block.&lt;/p&gt;
&lt;p&gt;We have to be careful to ensure that the including code gets run when and where all the values variables and functions used in the included fragment are in scope, which can be fiddly, but it&amp;rsquo;s definitely doable.&lt;/p&gt;
&lt;p&gt;&lt;span class="newthought"&gt;Which brings me to what I actually want to write about!&lt;/span&gt;
I don&amp;rsquo;t type all that &lt;code&gt;#+begin_src …&lt;/code&gt; stuff out by hand every time. Because doing that is simply asking for errors. Instead, I take advantage of an org feature called structure templates, so I have:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;#+begin_src emacs-lisp :tangle nil :noweb-ref org-structure-templates
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-emacs-lisp" data-lang="emacs-lisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;add-to-list&lt;/span&gt; &lt;span class="ss"&gt;&amp;#39;org-structure-template-alist&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;el&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;src emacs-lisp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;ett&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;src emacs-lisp :tangle nil :noweb-ref&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;#+end_src
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And, until slightly before I wrote this, I&amp;rsquo;d type &lt;code&gt;&amp;lt;el&lt;/code&gt; at the beginning of a line, hit TAB and org would expand that to&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;#+begin_src emacs-lisp
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;#+end_src
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And &lt;code&gt;&amp;lt;ett&lt;/code&gt; would expand to&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;#+begin_src emacs-lisp :tangle nil :noweb-ref
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;#+end_src
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then I&amp;rsquo;d fill in the correct &lt;code&gt;:noweb-ref&lt;/code&gt; and the appropriate code.&lt;/p&gt;
&lt;p&gt;It was fine.&lt;/p&gt;
&lt;p&gt;Well, it was better than typing it all by hand, but as soon as I&amp;rsquo;d expanded a template, I&amp;rsquo;d immediately hit &lt;code&gt;C-c '&lt;/code&gt; to edit the code block in a separate buffer that was in the correct mode to edit Emacs Lisp (or whatever language the source block was for).&lt;/p&gt;
&lt;p&gt;Eventually, I got annoyed enough by the repetition to work out how to make that happen automatically. Normally, I&amp;rsquo;d expect to add a function to a hook variable somewhere, but that doesn&amp;rsquo;t quite work here. Time to break out the Swiss Army Knife that is &lt;code&gt;advice-add&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s my first take:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-emacs-lisp" data-lang="emacs-lisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;+org-insert-structure-template/after-advice&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;&amp;amp;rest&lt;/span&gt; &lt;span class="nv"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;when&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;derived-mode-p&lt;/span&gt; &lt;span class="ss"&gt;&amp;#39;org-mode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;org-edit-special&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;advice-add&lt;/span&gt; &lt;span class="ss"&gt;&amp;#39;tempo-insert-template&lt;/span&gt; &lt;span class="nb"&gt;:after&lt;/span&gt; &lt;span class="nf"&gt;#&amp;#39;&lt;/span&gt;&lt;span class="nv"&gt;+org-insert-structure-template/after-advice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;advice-add&lt;/span&gt; &lt;span class="ss"&gt;&amp;#39;org-insert-structure-template&lt;/span&gt; &lt;span class="nf"&gt;#&amp;#39;&lt;/span&gt;&lt;span class="nv"&gt;org-edit-special&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;rsquo;re adding advice to two different functions here, because there are two different mechanisms for inserting a structure template, either via the &lt;code&gt;&amp;lt;foo&lt;/code&gt; expansion, or by calling &lt;code&gt;M-x org-insert-structure-template&lt;/code&gt;, both of which I make use of on different occasions.&lt;/p&gt;
&lt;p&gt;&lt;span class="newthought"&gt;This version lasted a while.&lt;/span&gt;
It did 90% of what I wanted after all. Indeed, for most of my structure templates, it does 100% of what I want.&lt;/p&gt;
&lt;p&gt;But there&amp;rsquo;s always that one case, isn&amp;rsquo;t there? Take a look at that &lt;code&gt;ett&lt;/code&gt; template from earlier. Notice that it&amp;rsquo;s missing the value to assign to &lt;code&gt;:noweb_ref&lt;/code&gt;. In a perfect world, we should fill that in before we start editing the code. Or rely on remembering to do it after the fact. Because we &lt;em&gt;always&lt;/em&gt; do that, don&amp;rsquo;t we?&lt;/p&gt;
&lt;p&gt;So, this morning, that chunk of code looked a little like this.
&lt;object role="note"&gt;&lt;p&gt;I’ve had to reconstruct the code from memory, I’m afraid because it never made it into the git repo. You can call me sloppy if you like, but this is personal code written on my own time, so you can fuck off.&lt;/p&gt;
&lt;/object&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-emacs-lisp" data-lang="emacs-lisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;+org-insert-structure-template/after-advice&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;&amp;amp;rest&lt;/span&gt; &lt;span class="nv"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;when&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;derived-mode-p&lt;/span&gt; &lt;span class="ss"&gt;&amp;#39;org-mode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;datum&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;org-element-begin&lt;/span&gt; &lt;span class="nv"&gt;datum&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;save-excursion&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;goto-char&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;org-element-begin&lt;/span&gt; &lt;span class="nv"&gt;datum&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;when&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;re-search-forward&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;\\(:\\S-+\\)\\s-*$&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;pos-eol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="no"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;key&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;match-string-no-properties&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;end-of-line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;unless&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;looking-back&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;\\s-&amp;#34;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt; &lt;span class="s"&gt;&amp;#34; &amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;read-from-minibuffer&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;%s: &amp;#34;&lt;/span&gt; &lt;span class="nv"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)))))))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;org-edit-special&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;advice-add&lt;/span&gt; &lt;span class="ss"&gt;&amp;#39;tempo-insert-template&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;:after&lt;/span&gt; &lt;span class="nf"&gt;#&amp;#39;&lt;/span&gt;&lt;span class="nv"&gt;+org-insert-structure-template/after-advice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;advice-add&lt;/span&gt; &lt;span class="ss"&gt;&amp;#39;org-insert-structure-template&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;:after&lt;/span&gt; &lt;span class="nf"&gt;#&amp;#39;&lt;/span&gt;&lt;span class="nv"&gt;+org-insert-structure-template/after-advice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Because an org file is Just A Text File™, we could have written this using Emacs&amp;rsquo; basic buffer editing commands, but we&amp;rsquo;ll take advantage of some of org and &lt;code&gt;org-babel&lt;/code&gt;&amp;rsquo;s helper functions to make life a little easier and (hopefully) to help me understand what I&amp;rsquo;m doing and why I&amp;rsquo;m doing it when I come back to the code later. Named behaviour is great
&lt;object role="note"&gt;&lt;p&gt;&lt;em&gt;Especially&lt;/em&gt; in a self-documenting editor like Emacs. If I’m not sure what a particular function in this code does, the documentation, or even the source code, is always a couple of keypresses away.&lt;/p&gt;
&lt;/object&gt;
for helping make code more understandable.&lt;/p&gt;
&lt;p&gt;What we do here is save our place in the buffer, jump back to the very beginning of the source block and look at the header arguments. If they end with a property name (&lt;code&gt;:like-this&lt;/code&gt;), then we deduce that more information is needed, so we use &lt;code&gt;read-from-minibuffer&lt;/code&gt; to ask for it, add the answer to the end of the header arguments, jump back to wherever the template originally left us (by exiting the &lt;code&gt;save-excursion&lt;/code&gt; block) and call &lt;code&gt;org-edit-special&lt;/code&gt; to start editing the file in a dedicated buffer.&lt;/p&gt;
&lt;p&gt;&lt;span class="newthought"&gt;Job jobbed, no?&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Well… kinda. See, we&amp;rsquo;re filling in the value of &lt;code&gt;:noweb-ref&lt;/code&gt; using an error prone free text value, rather than presenting a list of known noweb references to choose from. In the case where the unset parameter is &lt;code&gt;:noweb-ref&lt;/code&gt;, we really want to use &lt;code&gt;completing-read&lt;/code&gt;. A quick trawl through the existing code didn&amp;rsquo;t find a function to do what we want, nor did a web search. An org file&amp;rsquo;s Just A Text File though, and &lt;code&gt;org-babel-noweb-wrap&lt;/code&gt; returns a regular expression that will match a noweb reference in the current file
&lt;object role="note"&gt;&lt;p&gt;&lt;code&gt;&amp;lt;&amp;lt;reference&amp;gt;&amp;gt;&lt;/code&gt; is the default form for a noweb ref, but the delimiters are overridable. At one point, I was using &lt;code&gt;«reference»&lt;/code&gt; rather than the defaults, so it’s handy to have a function that deals with that for us.&lt;/p&gt;
&lt;/object&gt;
so we could save our place, jump to the beginning of the file and find every match for that regular expression and use that to build a &lt;code&gt;completing-read&lt;/code&gt; candidates list. But &lt;code&gt;&amp;lt;&amp;lt;foo&amp;gt;&amp;gt;&lt;/code&gt; is only a noweb reference if it&amp;rsquo;s in a source block, so we could end up with a bunch of false positives. We want to search through every source block, ignoring the rest of the file. Surely there&amp;rsquo;s already something in existence to let us do that since it&amp;rsquo;s the sort of thing that happens during the process of tangling a file.&lt;/p&gt;
&lt;p&gt;A quick &lt;code&gt;M-x describe-function org babel src block&lt;/code&gt; yields a bunch of interesting functions, including the promising sounding &lt;code&gt;org-babel-src-blocks&lt;/code&gt;. The documentation reads:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Signature
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;(org-babel-map-src-blocks FILE &amp;amp;rest BODY)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Documentation
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Evaluate BODY forms on each source-block in FILE.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;…
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It goes on to explain that the body is evaluated with some useful variables set. The one we&amp;rsquo;re interested in is &lt;code&gt;body&lt;/code&gt;, which is a &amp;ldquo;string holding the body of the code block&amp;rdquo;. Sorted.&lt;/p&gt;
&lt;p&gt;With that, and other helper functions, we can write:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-emacs-lisp" data-lang="emacs-lisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;+org-babel-noweb-refs&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;Find all the noweb refs in the current buffer.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="ss"&gt;&amp;#39;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="ss"&gt;&amp;#39;dash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;match-exp&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;org-babel-noweb-wrap&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;org-babel-map-src-blocks&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;plain-body&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;substring-no-properties&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;setq&lt;/span&gt; &lt;span class="nv"&gt;result&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;-concat&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;result&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;-map&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;-partial&lt;/span&gt; &lt;span class="nf"&gt;#&amp;#39;&lt;/span&gt;&lt;span class="nv"&gt;s-replace&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;(.*)\\&amp;#39;&amp;#34;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;-map&lt;/span&gt; &lt;span class="nf"&gt;#&amp;#39;&lt;/span&gt;&lt;span class="nv"&gt;second&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;s-match-strings-all&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;match-exp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;plain-body&lt;/span&gt;&lt;span class="p"&gt;)))))))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;-sort&lt;/span&gt; &lt;span class="nf"&gt;#&amp;#39;&lt;/span&gt;&lt;span class="nv"&gt;string&amp;lt;&lt;/span&gt; &lt;span class="nv"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It&amp;rsquo;s a bit unsubtle, but it&amp;rsquo;s quick enough and accurate enough for my purposes. Then we can rewrite the relevant bit of our advice function along these lines:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-emacs-lisp" data-lang="emacs-lisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;re-search-forward&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;\\(:\\S-+\\)\\s-*$&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;pos-eol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="no"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;let*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;arg&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;match-string-no-properties&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;value&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cond&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;string=&lt;/span&gt; &lt;span class="nv"&gt;arg&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;:noweb-ref&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;completing-read&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;:noweb-ref: &amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;+org-babel-noweb-refs&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;t&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;read-from-minibuffer&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt; &lt;span class="nv"&gt;arg&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;: &amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))))))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;end-of-line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;unless&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;looking-back&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;\\s-&amp;#34;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt; &lt;span class="s"&gt;&amp;#34; &amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;insert-value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note the use of &lt;code&gt;cond&lt;/code&gt; here even though we could use a single &lt;code&gt;if&lt;/code&gt;. I&amp;rsquo;m making it easier to special case behaviour for header arguments other than &lt;code&gt;:noweb-ref&lt;/code&gt;. I&amp;rsquo;m probably not gonna need it, but it&amp;rsquo;s easy enough to be kind to the future me who &lt;em&gt;does&lt;/em&gt; need it.&lt;/p&gt;
&lt;p&gt;&lt;span class="newthought"&gt;I’m sure your editor of preference&lt;/span&gt;
can do something like this. If it can&amp;rsquo;t, then why on earth do you put up with it? I know Emacs though, and it it&amp;rsquo;s taken me longer to write about this whole process of eliminating a bump in my road than it did to implement the necessary functions and advice in the first place. I didn&amp;rsquo;t even have to restart Emacs, and it remained usable throughout the process
&lt;object role="note"&gt;&lt;p&gt;The code was actually written within a source block&lt;/p&gt;
&lt;/object&gt;
but it got smoother and smoother with every step.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not the first person to point out how powerful Just A Text File can be, especially if you&amp;rsquo;ve also got a huge pile of functions at your disposal to manipulate that file in useful ways and with mode specific semantics. Provided, of course, you&amp;rsquo;ve got decent tools to search through that pile. Emacs is just such a pile of useful functions and is a great tool for sifting through it. Give it a go, why don&amp;rsquo;t you.&lt;/p&gt;
&lt;p&gt;Take the time to look at how you use your editor. I&amp;rsquo;m sure some point of friction will come to mind. Then work out how to write some code that will make things a bit smoother. You&amp;rsquo;re not looking for perfect here, you&amp;rsquo;re looking for &lt;em&gt;better.&lt;/em&gt; The remaining roughness will no doubt niggle at you enough for you to take another pass at sanding it down one day, but for now luxuriate in the fact you&amp;rsquo;ve made life a little better for yourself.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d suggest that, if you keep doing that as part of your practice, you&amp;rsquo;ll eventually have your tools working exactly how you want them to. But I&amp;rsquo;ve been an Emacs user since 1988 and I&amp;rsquo;ve yet to reach that point. But it&amp;rsquo;s about the journey, not the destination, isn&amp;rsquo;t it?&lt;/p&gt;
</content></entry><entry><title>A Guest's Guide to Zoom Song Swaps</title><link href="https://bofh.org.uk/2024/03/04/a-guests-guide-to-zoom-song-swaps/"/><id>https://bofh.org.uk/2024/03/04/a-guests-guide-to-zoom-song-swaps/</id><updated>2024-03-04T08:51:00Z</updated><summary>&lt;p&gt;This is intended as a quick guide for anyone who comes on as a Friday night Song Swap guest, but anyone who&amp;rsquo;s thinking of using Zoom (or doing their own streams) to share music with folk might find it useful, so I&amp;rsquo;m making it public.&lt;/p&gt;</summary><content type="html">&lt;p&gt;This is intended as a quick guide for anyone who comes on as a Friday night Song Swap guest, but anyone who&amp;rsquo;s thinking of using Zoom (or doing their own streams) to share music with folk might find it useful, so I&amp;rsquo;m making it public.&lt;/p&gt;
&lt;h2 id="publicity-material"&gt;Publicity material&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;m absolutely terrible at marketing, but it will definitely help me do at least &lt;em&gt;some&lt;/em&gt; if you can provide me with some or all of the following:&lt;/p&gt;
&lt;h3 id="a-photo-of-you-that-you-don-t-hate"&gt;A photo of you that you don&amp;rsquo;t hate&lt;/h3&gt;
&lt;p&gt;If you&amp;rsquo;ve got a standard headshot or publicity photo, that&amp;rsquo;s great. I&amp;rsquo;ll use that to make the YouTube preview card.&lt;/p&gt;
&lt;p&gt;If you don&amp;rsquo;t, here&amp;rsquo;s a few tips for taking your own. These are basic guidelines, not rules. You can ignore all of them and still get a great photo, but if you follow them, you should at least get a decent one.&lt;/p&gt;
&lt;h4 id="your-phone-is-fine"&gt;Your phone is fine&lt;/h4&gt;
&lt;p&gt;Seriously, I would have killed for something the quality of even my iPhone SE back when I first started doing digital photography.&lt;/p&gt;
&lt;h4 id="try-to-enjoy-yourself"&gt;Try to enjoy yourself&lt;/h4&gt;
&lt;p&gt;If you don&amp;rsquo;t like having your photo taken, it can show and you&amp;rsquo;ll end up with another photo you don&amp;rsquo;t like, so try this. For the duration of the shoot, pretend you&amp;rsquo;re playing the part of someone who actually likes having their photo taken. Weirdly, that will almost certainly make the shoot go faster because you&amp;rsquo;ll get a good shot nice and early and you can stop pretending.&lt;/p&gt;
&lt;h4 id="get-a-friend-to-take-the-photo"&gt;Get a friend to take the photo&lt;/h4&gt;
&lt;p&gt;It&amp;rsquo;s just easier. You can use a tripod or a selfie stick, but another human being&amp;rsquo;s your best choice. If you&amp;rsquo;ve got a friend who&amp;rsquo;s an experienced photographer, then for heaven&amp;rsquo;s sake ask them and ignore the rest of this list &amp;ndash; be guided by them. Or book a session with a professional if you have the time, money and desire.&lt;/p&gt;
&lt;h4 id="use-a-big-light"&gt;Use a big light&lt;/h4&gt;
&lt;p&gt;&lt;em&gt;Big&lt;/em&gt; light sources are the most forgiving and flattering ones. I like a nice big north facing window, or bright overcast days. It&amp;rsquo;s not exciting or dramatic light, but that&amp;rsquo;s fine.&lt;/p&gt;
&lt;h4 id="it-s-not-a-firing-squad"&gt;It&amp;rsquo;s not a firing squad&lt;/h4&gt;
&lt;p&gt;You know the typical passport photo shot? The one where you look like you&amp;rsquo;ve been lined up against a wall, standing square to the camera and looking nervous. Don&amp;rsquo;t do that.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a pretty foolproof lighting/posing idea:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Stand facing the biggest window you can find at a time when the sun isn&amp;rsquo;t shining directly through it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Turn about 45° to the left or right&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Look across at your friend with the camera who&amp;rsquo;s standing at 90° to the window light.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Your friend will, of course, take an otherwise fabulous photograph of you with your eyes closed, so repeat the last step until they manage a shot you like.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;CHECK YOUR BACKGROUND!&lt;/strong&gt;
Make sure there&amp;rsquo;s no dead flowers apparently growing out of your ears, an embarrasing cat licking its arse, or a pile of washing in the shot.&lt;/p&gt;
&lt;p&gt;Ideally you&amp;rsquo;ll have a nice smooth out of focus thing going on there (portrait mode, if your phone has it can help), but if you can&amp;rsquo;t manage that, shoot for something that&amp;rsquo;s not too busy. You can&amp;rsquo;t go far wrong with a bookshelf, but stand as far away from it as you can so it&amp;rsquo;s not tack sharp and distracting.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="portrait-or-square-format-please"&gt;Portrait or square format, please&lt;/h4&gt;
&lt;p&gt;Bear in mind that I&amp;rsquo;m going to be putting your photo into a square frame in the YouTube title card, so a wide image probably won&amp;rsquo;t work too well.&lt;/p&gt;
&lt;h3 id="some-links"&gt;Some links&lt;/h3&gt;
&lt;p&gt;Got a website? Bandcamp? YouTube channel? Tell me about them and I&amp;rsquo;ll link to them in the event description and during the show.&lt;/p&gt;
&lt;h3 id="a-short-biography"&gt;A short biography&lt;/h3&gt;
&lt;p&gt;When I make the YouTube event for a song swap, I write something about my guests. I&amp;rsquo;ll write something anyway, but it really helps if I&amp;rsquo;ve got something from you to base it on. After all, I know what I think about you, and I like you or I&amp;rsquo;d not have asked you to be my guest, but unless you &lt;em&gt;tell&lt;/em&gt; me, I don&amp;rsquo;t know what you think is important about yourself. A short bio can really help there.&lt;/p&gt;
&lt;h3 id="optional-some-talking-points"&gt;[Optional] Some talking points&lt;/h3&gt;
&lt;p&gt;A song swap isn&amp;rsquo;t some kind of forensic interview process; it&amp;rsquo;s supposed to be an informal chat interspersed with songs. If there&amp;rsquo;s something you really want to talk about, please let me know. The same goes if there&amp;rsquo;s anything you want me to steer clear of. It&amp;rsquo;s fine if the conversation gets dark &amp;ndash; folk music&amp;rsquo;s full of murders and misogyny after all &amp;ndash; but it&amp;rsquo;s really not fine if it gets uncomfortable for you.&lt;/p&gt;
&lt;h2 id="tech"&gt;Tech&lt;/h2&gt;
&lt;p&gt;Zoom isn&amp;rsquo;t my favourite piece of software and its default settings are definitely slanted towards business meetings rather than helping musicians sound good. The most important thing you can do in your Zoom settings is to turn on &amp;lsquo;Original Sound For Musicians&amp;rsquo;, which should show up in the top left of your screen. It&amp;rsquo;s off by default, so click it and turn it on. If it doesn&amp;rsquo;t show, you&amp;rsquo;ll need to dig into Zoom&amp;rsquo;s audio settings and check the box to make it available.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a catch: turning on original sound disables &lt;em&gt;all&lt;/em&gt; of Zoom&amp;rsquo;s audio processing, including the echo cancellation magic, so it&amp;rsquo;s really best if you can use a pair of headphones or earbuds rather than speakers to hear me. Or you can just remember to turn original sound on when you start singing and off again when we start chatting. Headphones are easier though.&lt;/p&gt;
&lt;p&gt;If your laptop or phone&amp;rsquo;s built in camera and microphone are all you have, don&amp;rsquo;t worry, we can work with that. They&amp;rsquo;re not the best, but they are optimised for someone sitting within reach of the keyboard and making human noises from their mouth hole. You&amp;rsquo;ll look and sound fine.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve got money to spend and you want to look and sound better on stream then I have a few suggestions, but this is an area where there&amp;rsquo;s no right answers, so do shop around and talk to anyone you know who you reckon looks good on your Zoom calls.&lt;/p&gt;
&lt;p&gt;Prioritize your spending on gear. In general (and especially for musicians) sound is more important than lighting, which is more important than camera quality. A pin sharp, beautifully lit video of a muddy sounding performer is much harder to watch than a blurry, crystal clear sounding performer singing in a murky cave.&lt;/p&gt;
&lt;h3 id="some-audio-suggestions"&gt;Some audio suggestions&lt;/h3&gt;
&lt;p&gt;If you&amp;rsquo;re an unaccompanied singer, this is pretty easy because we don&amp;rsquo;t have any problems getting your sound balanced and into Zoom. If you&amp;rsquo;re an instrumentalist, or a band, things get a little more fun, so let&amp;rsquo;s break it down based on the number of mics you&amp;rsquo;ll need.&lt;/p&gt;
&lt;p&gt;Bear in mind that there are entire books written on this subject; I&amp;rsquo;m barely scratching the surface here. If you&amp;rsquo;re serious about getting good sound for streaming and/or recording, it&amp;rsquo;s worth doing your own research. If all else fails, for your upcoming song swap, buy some of the gear recommended here from a reputable mail order site and rely on the 30 day, no questions asked returns policy they all have because that&amp;rsquo;s a legal requirement in the UK. But MAKE SURE that when you know what you &lt;em&gt;really&lt;/em&gt; want, you buy from the same supplier.&lt;/p&gt;
&lt;h4 id="one-mic-wonders"&gt;One mic wonders&lt;/h4&gt;
&lt;p&gt;If the sound you make in the room is the sound you&amp;rsquo;re happy with (unaccompanied singers, acoustic guitarists, acoustic bands) then it&amp;rsquo;s just a matter of choosing between a USB mic and an XLR mic with an audio interface. Unless you&amp;rsquo;re in a particularly noisy environment, I&amp;rsquo;d recommend some kind of condenser mic. The Blue Yeti has been the standard starter USB mic for years and you could do far worse than do that yourself. However, I would definitely recommend going down the slightly more expensive XLR mic and audio interface route as it&amp;rsquo;s significantly more flexible and upgradeable.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll want a cardioid pattern large diaphragm condenser microphone (I love my Aston Spirit which looks and sounds great, but it&amp;rsquo;s a multi-pattern mic and nearly £300 new). Brands like Rode, Aston and SE Electronics make great mics, and honestly, pretty much anything that comes up on an Amazon search for &amp;rsquo;large diaphragm condenser microphone XLR&amp;rsquo; will still sound better than your laptop or webcam&amp;rsquo;s microphone. You&amp;rsquo;ll use a balanced XLR cable to connect that to your interface (a cheap cable&amp;rsquo;s fine, more expensive ones with Neutrik brand connectors and the like might prove more durable and/or reassuring.)&lt;/p&gt;
&lt;p&gt;With a single mic setup you only &lt;em&gt;need&lt;/em&gt; a really basic audio interface. Something like the Focusrite Scarlett Solo or 2i2 or any cheap class compliant USB audio interface will do the trick so long has it has phantom power available. Just plug your interface in, connect the mic, turn on 48v/phantom power and tweak the gain until, at your loudest you&amp;rsquo;re not quite going into the red on the meters, select the interface in Zoom, turn on direct monitoring and you&amp;rsquo;re good.&lt;/p&gt;
&lt;h4 id="fun-with-multiple-inputs"&gt;Fun with multiple inputs&lt;/h4&gt;
&lt;p&gt;If you&amp;rsquo;re a band, or a guitarist where you want to adjust the balance between your voice and your guitar, you&amp;rsquo;re going to need something a little more sophisticated. How much more sophisticated is up to you, of course and I&amp;rsquo;m a little out of my depth here, but I&amp;rsquo;ve got a few suggestions anyway. Don&amp;rsquo;t hesitate to chat to any live sound engineers of your acquaintance &amp;ndash; buy the sound engineer at your local open mic a drink and quiz them, for instance.&lt;/p&gt;
&lt;p&gt;The thing to remember here is that Zoom is pretty crap when it comes to audio handling &amp;ndash; it doesn&amp;rsquo;t know anything about pro audio gear, it just expects to receive a mono mix on the first channel of the audio device you select (or a stereo mix on channels 1&amp;amp;2 if you turn stereo on in the audio settings), so you&amp;rsquo;ll need to do the mixing yourself, either with a standalone mixer, the facilities of your audio interface or some other software on your computer.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m going to ignore the software option, but investigate software like Loopback on the Mac and Voicemeeter on Windows. Using your audio interface can be more or less easy, depending on what capabilities the interface has, of course. Modern interfaces are generally more capable in this area. I&amp;rsquo;ve not actually tried it, but something like the Zoom AMS-24 looks like it would be ideal for a guitarist (put it in &amp;lsquo;streaming&amp;rsquo; mode and turn off loopback).&lt;/p&gt;
&lt;p&gt;Once you get past a couple of input channels, you&amp;rsquo;re going to have to go down the mixer route. You can either get a dumb mixer with at least as many inputs as you have instruments and mics, sort out your stereo mix and feed that into a simple two channel audio interface, which will make Zoom happy. That&amp;rsquo;s fine if you&amp;rsquo;re primarily interested in live performance, but if you want to do any recording, you&amp;rsquo;d be better off with a mixer that can also work as a multi-channel audio interface so you can record your vocals and instruments in separate tracks. Something like the Roland GigCasters or the RodeCaster II, for instance. I think Mackie do something in this space too, but I&amp;rsquo;ve not used any myself, but I do know there are plenty of options.&lt;/p&gt;
&lt;p&gt;If your guitar&amp;rsquo;s got a built in mic or pickup, just plug it into your mixer with a balanced cable, otherwise, you&amp;rsquo;ll need to mic it up too. There&amp;rsquo;s whole books written on mic placement, but it&amp;rsquo;s generally accepted that a good starting point for micing an acoustic guitar is to point the mic at the place where the neck meets the body rather than at the sound hole, maybe a foot or so away. Again, if you know anyone with any audio engineering experience, then talk to them not me.&lt;/p&gt;
&lt;p&gt;Once you&amp;rsquo;ve got your mixer, you&amp;rsquo;ll need mics. Generally you&amp;rsquo;ll be close miking things to allow you to mix the different sound sources (if you get lots of bleed between mics, then you have fewer options when it comes to mixing). In &lt;em&gt;theory&lt;/em&gt; you use a condenser mic for all these things, just put them close to your mouth or mic, turn the gain down and rely on the inverse square law to give you some separation. In practice, it&amp;rsquo;s more common to grab a dynamic microphone or two and use them. The canonical mic for the job is probably the Shure SM58, which is built like a tank and looks exactly like you think a stage microphone should look. The SM57 is well regarded as an instrument mic too, and there&amp;rsquo;s plenty of knock-offs of both. I have an SE Electronics V7, that I use for open mics which sounds pretty good too.&lt;/p&gt;
&lt;p&gt;It &lt;em&gt;really&lt;/em&gt; helps to have someone else fiddling with the knobs to get your sound dialled in, because they&amp;rsquo;re not hearing the sound in your head. With decent headphones on, they&amp;rsquo;re primarily hearing the sound that&amp;rsquo;s going through the mixer, so they can have a better chance of getting pre-amp gain dialed in, EQing your voice an instruments so they don&amp;rsquo;t overlap too much and balancing your levels nicely. If &lt;em&gt;at all&lt;/em&gt; possible, get some help here.&lt;/p&gt;
&lt;p&gt;EQing is a dark art that I&amp;rsquo;m only vaguely aware of, and mostly don&amp;rsquo;t have to worry about anyway as an unaccompanied singer. I mostly just leave things flat and hope. There&amp;rsquo;s plenty of advice to be found on YouTube or sites like Sound On Sound, so I recommend investigating those.&lt;/p&gt;
&lt;h3 id="lighting-suggestions"&gt;Lighting Suggestions&lt;/h3&gt;
&lt;p&gt;Again, there are entire books on this, and there&amp;rsquo;s no end of gear you can buy if really fall down the rabbit hole. Elly Lucas made a great &lt;a href="https://youtu.be/fYYzN2E8eX4"&gt;&amp;lsquo;Visual Content Level Up Tutorial&amp;rsquo;&lt;/a&gt; that&amp;rsquo;s a great starting point.&lt;/p&gt;
&lt;p&gt;Seriously. Just watch that. I was going to write more, but she covers pretty everything I was going to say.&lt;/p&gt;
&lt;h3 id="cameras"&gt;Cameras&lt;/h3&gt;
&lt;p&gt;If at all possible, don&amp;rsquo;t use the webcam in your laptop. Investigate ways to use your phone as a webcam. Certainly that&amp;rsquo;s possible if you&amp;rsquo;re in Apple World &amp;ndash; Zoom can treat your iPhone as a webcam and the camera in your phone is substantially better than almost any webcam you can find. If you&amp;rsquo;ve got a mirrorless or dSLR camera, more recent ones often have software that lets you use them as a webcam &amp;ndash; check your manufacturers website. If they can&amp;rsquo;t be used directly as a webcam, check whether they have what&amp;rsquo;s referred to as a &amp;lsquo;clean HDMI&amp;rsquo; output and look at getting a cheap and cheerful usb HDMI capture card (or spend rather more on something like the Elgato Camlink. I went with the cheap and cheerful option and it&amp;rsquo;s fine).&lt;/p&gt;
&lt;p&gt;Stick your camera or phone on a good solid tripod, wobbly cameras are really distracting. And level is great. You don&amp;rsquo;t want people wondering why the things on your shelves aren&amp;rsquo;t sliding off. Plain backgrounds make this less critical.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re shooting for a solo video, you probably want to frame things so you&amp;rsquo;re slightly off centre in the frame. However, it makes life much easier for me setting up the Song Swap if you frame yourself in the middle of the shot. One of these days I might fix things so that&amp;rsquo;s not necessary, but for now centred is best.&lt;/p&gt;
&lt;h3 id="set-dressing"&gt;Set dressing&lt;/h3&gt;
&lt;p&gt;Plain walls are great. If you can arrange to hang something like a duvet behind you, that acts as quick and dirty audio treatment of your room. I have a duvet and a rug hanging behind me as a background and it definitely helps with the sound. Getting some distance between you and your background helps blur it a little and make it less distracting.&lt;/p&gt;
&lt;p&gt;The advice on making your own headshot applies here as well.&lt;/p&gt;
&lt;h2 id="what-to-expect"&gt;What to expect&lt;/h2&gt;
&lt;p&gt;The aim of a song swap is to have a good old natter and sing a bunch of songs and to enjoy ourselves while we do it. It&amp;rsquo;s not an interview, if only because I&amp;rsquo;m a terrible gobshite with a tendency to go off on tangents. Don&amp;rsquo;t hesitate to tell me to shut up. I try to rein myself in, but every time I watch a show back I think &amp;ldquo;Yeah, you could have shut up a bit more there Piers.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re the sort who likes a set list, I&amp;rsquo;d suggest planning to cover as much material as you&amp;rsquo;d get through in a forty minute folk club set with maybe a couple of encore pieces. Nobody&amp;rsquo;s run out of material yet.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s a song swap tradition that the first question I ask a new guest is to ask them to talk about their first encounter with Martin Carthy, whether in person or recorded. Chris Manners memorably &lt;a href="https://youtu.be/_wjZAKqdZdg"&gt;described first hearing Martin singing &lt;em&gt;The Bedmaking&lt;/em&gt; on the John Peel show&lt;/a&gt; playing a guitar &amp;lsquo;hard enough to drive rivets through concrete walls&amp;rsquo; and rearranging his entire world. My first encounter was at the Soles and &amp;lsquo;Eels folk club in Northampton. Everyone had hyped the gig up beforehand like it was the Second Coming and I bloody &lt;em&gt;hated&lt;/em&gt; it. Martin was great, as usual, I just wasn&amp;rsquo;t in the right place to realise that. We&amp;rsquo;re not about gatekeeping here though, so if you don&amp;rsquo;t know who I&amp;rsquo;m talking about, let me know and I&amp;rsquo;ll probably ask you how you came to discover you were a musician.&lt;/p&gt;
&lt;h3 id="payment"&gt;Payment&lt;/h3&gt;
&lt;p&gt;The show is supported by people chucking money in the virtual hat at &lt;a href="https://ko-fi.com/pdcawley"&gt;https://ko-fi.com/pdcawley&lt;/a&gt;. On the following Monday, I tot up the total takings and send you half through the magic of Paypal.&lt;/p&gt;</content></entry><entry><title>Panning Overdubs in Loopy Pro</title><link href="https://bofh.org.uk/2024/01/13/panning-overdubs-in-loopy-pro/"/><id>https://bofh.org.uk/2024/01/13/panning-overdubs-in-loopy-pro/</id><updated>2024-01-13T18:03:00Z</updated><summary>&lt;p&gt;It&amp;rsquo;s common practice in music production that, when you&amp;rsquo;re layering your vocals with harmonies or doubles, you pan everything but the lead vocal around the stereo field. That gives a sense of physical separation between the voices and it feels more realistic &amp;ndash; or artificial, if that&amp;rsquo;s what you&amp;rsquo;re going for.&lt;/p&gt;</summary><content type="html">&lt;p&gt;It&amp;rsquo;s common practice in music production that, when you&amp;rsquo;re layering your vocals with harmonies or doubles, you pan everything but the lead vocal around the stereo field. That gives a sense of physical separation between the voices and it feels more realistic &amp;ndash; or artificial, if that&amp;rsquo;s what you&amp;rsquo;re going for.&lt;/p&gt;
&lt;p&gt;With loopers though, you&amp;rsquo;ll more usually have your overdubs all in the same place, which is fine at the local open mic, where the front of house PA is often mono anyway, but for streamers like me, it feels a little limiting, so I&amp;rsquo;ve been working on a way to get that effect.&lt;/p&gt;
&lt;p&gt;This is still a work in progress, but enough people expressed an interest when I showed this off in a &lt;a href="https://www.youtube.com/live/uaP4np4WoAY?si=zPoXvCthRjsDUKEn"&gt;&amp;ldquo;How Do You Loop?&amp;rdquo;&lt;/a&gt; chat with John Paul Music UK here.&lt;/p&gt;
&lt;h2 id="quickstart"&gt;Quickstart&lt;/h2&gt;
&lt;p&gt;If you don&amp;rsquo;t want the gory details of how the pandomiser works, here&amp;rsquo;s how to add it to your Loopy Pro and get going quickly:&lt;/p&gt;
&lt;h3 id="set-up-mirack-in-your-project"&gt;Set up miRACK in your project&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Install &lt;a href="https://apps.apple.com/gb/app/mirack/id1468259834"&gt;miRACK&lt;/a&gt; from the App store. It&amp;rsquo;s a full-featured modular synthesizer simulator, and it&amp;rsquo;s capable of far more and weirder things than we&amp;rsquo;re making it do here. Check out the examples.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Grab the pandomizer bundle from &lt;a href="/files/pandomiser.zip"&gt;my website&lt;/a&gt; and unzip with the Files app on your iDevice. Copy pandomiser.mrk to your miRACK folder so the plugin can find it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open the mixer, tap the ⊕ button and choose &amp;lsquo;Add MIDI&amp;rsquo; and choose the miRack &amp;lsquo;MIDI FX&amp;rsquo; option&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open the miRACK interface and tap on the four squares icon in the top left, choose &amp;lsquo;Open Existing&amp;rsquo; and load &lt;code&gt;pandomiser.mrk&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="set-up-loopy-s-audio-routing"&gt;Set Up Loopy&amp;rsquo;s Audio Routing&lt;/h3&gt;
&lt;p&gt;I&amp;rsquo;m a pure vocal looper, so I&amp;rsquo;m going to assume you have a single audio input for your vocals that&amp;rsquo;s routed to all Loopy&amp;rsquo;s colours. If you&amp;rsquo;ve got seperate inputs for your voice and other instruments and not everything is routed to all the colours, you might have to add multiple panning busses, but what follows should give you enough information to get started. This setup also assumes that you don&amp;rsquo;t want to affect the panning of your live audio, only where it gets placed in your loops and oneshots.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add a send to your audio input, choose &amp;rsquo;new bus&amp;rsquo; when prompted, then long press on the send and choose &amp;lsquo;After All Effects&amp;rsquo;&lt;/li&gt;
&lt;li&gt;Tap &amp;lsquo;Destinations&amp;rsquo; in the new bus and turn off the hardware output channels. Tap the &amp;lsquo;all&amp;rsquo; button to turn on all the Color Destinations.&lt;/li&gt;
&lt;li&gt;Tap &amp;lsquo;Destinations&amp;rsquo; on your audio input and &amp;rsquo;none&amp;rsquo; to turn off all the Color Destinations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now your input audio is going to your loops via this new bus, which means we can mess with its balance setting and it won&amp;rsquo;t affect the live sound going to your speakers. We can do &lt;em&gt;anything&lt;/em&gt; here, and it won&amp;rsquo;t be heard until the clip it&amp;rsquo;s recorded on gets played out. Use this power responsibly. All we&amp;rsquo;re going to do here is mess with the &amp;lsquo;balance&amp;rsquo; setting.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s go.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Choose &amp;lsquo;Control Settings&amp;rsquo; from the hamburger menu in the top right of Loopy&amp;rsquo;s screen, then choose &amp;lsquo;Default&amp;rsquo; from the Current Project setting.&lt;/li&gt;
&lt;li&gt;Tap &amp;lsquo;Add New Binding&amp;rsquo; and choose &amp;lsquo;Adjust Parameter&amp;rsquo; from the &lt;strong&gt;Bus Actions&lt;/strong&gt; section. Choose your new bus as the target, and make &amp;lsquo;Balance&amp;rsquo; the parameter.&lt;/li&gt;
&lt;li&gt;Change &lt;strong&gt;Controller Input Start&lt;/strong&gt; to 1%. This avoids the problem that 0-127 doesn&amp;rsquo;t have a central value. If you don&amp;rsquo;t change this, then the bus&amp;rsquo;s balance will get reset to 1 and, if you&amp;rsquo;re anything like me, it will drive you up the wall.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Test everything is working by opening the mixer and bringing up the keyboard in the miRACK window then holding down C4. You should see your panning bus&amp;rsquo;s balance jumping to a new random value, then resetting to the centre each time you press and release C4.&lt;/p&gt;
&lt;h2 id="basic-pandomiser-use"&gt;Basic Pandomiser use&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s one simple way of ensuring that all your overdubs are placed at different points in the stereo field using a couple of follow actions.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Open &lt;strong&gt;Clip Settings&lt;/strong&gt; from main menu, scroll down the settings sheet and choose &lt;strong&gt;Follow Actions&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Add an action to &lt;strong&gt;Begin Overdub,&lt;/strong&gt; choose &lt;strong&gt;Send MIDI Message&lt;/strong&gt; with your miRACK as the target. Send a note, C4 on channel 1 with velocity (&lt;strong&gt;Value&lt;/strong&gt; in the Loopy UI) 127 (those all the defaults for sending a note, by the way).&lt;/li&gt;
&lt;li&gt;Go back to Follow Actions and tap the &amp;lsquo;Reorder&amp;rsquo; button, now press and hold the follow action you just added and drag the new action down to &lt;strong&gt;Finish Overdub&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Open the new action and drag the &lt;strong&gt;Value&lt;/strong&gt; slider down to zero.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, whenever you overdub a clip, as soon as your overdubbing starts, Loopy will &amp;lsquo;hold down&amp;rsquo; C4 and the pandomiser will set the recording balance to a new random value. Once overdubbing stops, Loopy will &amp;lsquo;release&amp;rsquo; the key and the balance will return to the centre.&lt;/p&gt;
&lt;h2 id="complicating-things"&gt;Complicating things&lt;/h2&gt;
&lt;p&gt;In my Loopy setup, I don&amp;rsquo;t want to change the balance of every overdub of every clip. There might be a couple of clips that will be overdubbed for each verse of the song, and I want the pan to remain the same for each overdub associated with the verse. So I can set the first clip to send the &lt;code&gt;noteOn&lt;/code&gt; message when I start overdubbing and set the second to send a &lt;code&gt;noteOff&lt;/code&gt; when I finish.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; You might only want to mess with the panning for certain colours, or even for specific clips, or you might want to set up widgets to record with or without messing with the panning. All you have to do is send C4 to miRACK when you want to randomise the balance, and release C4 when you want it reset to the centre. Have fun.&lt;/p&gt;
&lt;h2 id="how-does-it-all-work"&gt;How does it all work?&lt;/h2&gt;
&lt;p&gt;A modular synthesizer like miRACK thinks in terms of voltages, and those voltages can mean different things. We use a midi trigger input module that we&amp;rsquo;ve configured to send +10V whenever C4 is held and we connect that to the &lt;strong&gt;Gate&lt;/strong&gt; input of a Sample &amp;amp; Hold (S&amp;amp;H) module, which we&amp;rsquo;re using as a source of randomness. Whenever the modules &lt;strong&gt;Gate&lt;/strong&gt; voltage goes high, the module &amp;lsquo;samples&amp;rsquo; the voltage at its input, and sets its output voltage to the same until the next time it detects a rising edge. If there&amp;rsquo;s nothing connected to the input though, it &lt;em&gt;samples&lt;/em&gt; an internal noise generator and outputs that voltage. I&amp;rsquo;ve configured the module to so that its noise source is a white noise generator a range of ±5V, and we can think of that as ranging from a hard left pan at -5V through the centre at 0V and on to a hard right pan at +5V.&lt;/p&gt;
&lt;p&gt;The output of the S&amp;amp;H module is now jumping to a new random value every time we press C4, but we really want to output 0V when C4 isn&amp;rsquo;t pressed, so we feed its output into the input of another staple of a modular setup, a Voltage Controlled Amplifier (VCA). We&amp;rsquo;ve set this VCA up at unity gain, which means that, when it sees 10V at its Control Voltage (CV) input, it outputs, it sends 100% of its input voltage to its output, and when it sees 0V CV, it sends 0% of its input. So, if we connect the C4 trigger to the VCA&amp;rsquo;s CV input, we&amp;rsquo;ve turned it into a gate &amp;ndash; whenever C4 is held, the VCA sends the random voltage from S&amp;amp;H, otherwise it sends 0V.&lt;/p&gt;
&lt;p&gt;Now we just need to convert that into something MIDI understands, an unsigned value between 0 and 127. The &lt;strong&gt;MIDI CC Output&lt;/strong&gt; module can do some of this for us, but it&amp;rsquo;s expecting a voltage in the range 0&amp;ndash;10V, and right now we&amp;rsquo;ve got something in the range of ±5V. So we feed the signal from the VCA into the A input of a &lt;strong&gt;CONST ADD MULT&lt;/strong&gt; module, set the constant to 5V, and feed the associated A+B output to one of our CC outputs that we&amp;rsquo;ve configured to send CC8 to our host app. Job jobbed!&lt;/p&gt;
&lt;h2 id="next-steps"&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;I think the next version of this will allow me to sequence balance for the first few steps, so it might go: hard left, hard right, mid left, mid right, then random pans until the system is reset. I have ideas about how to implement this too, but if you beat me too it, please let me know!&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll also be making a YouTube video walking you through it (think of this as a draft script for that video).&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;I don&amp;rsquo;t actually do that, because I have a slightly more complex setup that supports multiple song structures with the same set of clips. I&amp;rsquo;ll explain all that later.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</content></entry><entry><title>Week ending 2023-09-24</title><link href="https://bofh.org.uk/2023/09/24/week-note/"/><id>https://bofh.org.uk/2023/09/24/week-note/</id><updated>2023-09-24T10:47:00Z</updated><summary>&lt;p&gt;I&amp;rsquo;m not quite sure where the time went these last couple of weeks, but here we are at another Sunday. We&amp;rsquo;ve spent quite a bit of the time watching a new family of kittens that have taken to playing on the flat roof of our garage. They&amp;rsquo;re almost obscenely cute scraps of black and white fur and just delightful to watch.&lt;/p&gt;</summary><content type="html">&lt;p&gt;I&amp;rsquo;m not quite sure where the time went these last couple of weeks, but here we are at another Sunday. We&amp;rsquo;ve spent quite a bit of the time watching a new family of kittens that have taken to playing on the flat roof of our garage. They&amp;rsquo;re almost obscenely cute scraps of black and white fur and just delightful to watch.&lt;/p&gt;
&lt;h2 id="sunday"&gt;Sunday&lt;/h2&gt;
&lt;p&gt;Back in 2012, we were living in Cornwall and used to go to the regular Farmers&amp;rsquo; Markets in Mullion and Helston. One week, at Mullion, a new trader showed up selling home made bean to bar chocolate under the name of &lt;a href="https://chocolarder.com/"&gt;Chocolarder&lt;/a&gt;. I got chatting with Mike, the guy who made the stuff, and bought a few bars and some of his &lt;a href="https://www.chocolarder.com/shop/sea-salt-caramel-truffles/"&gt;sea salt caramel truffles&lt;/a&gt;. If we weren&amp;rsquo;t actually his first customer, we were damned close.&lt;/p&gt;
&lt;p&gt;Every month, he showed up with plain looking bars of &lt;em&gt;amazing&lt;/em&gt; chocolate. One time he&amp;rsquo;d bought a bunch of rose petals (apparently, they can be had quite reasonably after Valentine&amp;rsquo;s Day because there&amp;rsquo;s something of a glut), dessicated them, ground them fine and added them to the chocolate. Bloody delicious!&lt;/p&gt;
&lt;p&gt;I told Mike about some milk chocolate with sea salt that I&amp;rsquo;d tried and really loved and suggested he do something similar. He&amp;rsquo;d have a go he said. Months later (after many experiments, apparently) there he was with some bars of salted milk chocolate so I bought as many as I had the cash for and loved every mouthful. He didn&amp;rsquo;t make them again though. Except, today, with a bit of birthday money burning a hole in my pocket, I thought &amp;ldquo;I&amp;rsquo;d love some Chocolarder chocolate, it&amp;rsquo;s been an age&amp;rdquo; and what did they have? You guessed it: &lt;a href="https://www.chocolarder.com/shop/sea-salt-milk/"&gt;Cornish sea salt&lt;/a&gt; milk chocolate. So that&amp;rsquo;s a chunk of birthday money spent.&lt;/p&gt;
&lt;p&gt;Yes, it&amp;rsquo;s a lot of money for a bar of chocolate, but believe me, it&amp;rsquo;s amazing stuff and Mike is as committed to ethical and sustainable manufacture as anyone I&amp;rsquo;ve ever met. We&amp;rsquo;ve visited the factory a couple of times and I remember the time we visited and he was more excited about showing off his new, plastic free packaging as he was about the chocolate. He buys direct from cacao farmers and has been known to get his beans shipped by sail rather than container ship.&lt;/p&gt;</content></entry><entry><title>Week ending 2023-09-03</title><link href="https://bofh.org.uk/2023/09/03/week-note/"/><id>https://bofh.org.uk/2023/09/03/week-note/</id><updated>2023-09-03T13:47:00Z</updated><summary>&lt;p&gt;Another quiet week. Streamed tired on Friday night, so quite a bit of pilot error with the looper, but folk still seemed to enjoy it.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Another quiet week. Streamed tired on Friday night, so quite a bit of pilot error with the looper, but folk still seemed to enjoy it.&lt;/p&gt;
&lt;p&gt;Scrooby show was lovely on Saturday. Nice to catch up with a few folk I&amp;rsquo;ve not seen in quite a while and the weather was flat out gorgeous. I took some modelling balloons, planning to do a few balloon animals and hats, had something like a 60% burst rate with the Sempertex 260Ss that were all I could get hold of at short notice. Won&amp;rsquo;t be using those again. Qualatex all the way, I think.&lt;/p&gt;
</content></entry><entry><title>Week ending 2023-08-27</title><link href="https://bofh.org.uk/2023/08/27/week-note/"/><id>https://bofh.org.uk/2023/08/27/week-note/</id><updated>2023-08-27T12:32:00Z</updated><summary>&lt;p&gt;A bit of a mixed week, mood wise. Capped by a great day&amp;rsquo;s singing and chatting in Peterborough yesterday.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s great to get out of the house sometimes. Gill coped really well by herself too &amp;ndash; I&amp;rsquo;m a full time carer, but it&amp;rsquo;s definitely good to know that I can have the odd day off without it completely buggering things up.&lt;/p&gt;</summary><content type="html">&lt;p&gt;A bit of a mixed week, mood wise. Capped by a great day&amp;rsquo;s singing and chatting in Peterborough yesterday.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s great to get out of the house sometimes. Gill coped really well by herself too &amp;ndash; I&amp;rsquo;m a full time carer, but it&amp;rsquo;s definitely good to know that I can have the odd day off without it completely buggering things up.&lt;/p&gt;
&lt;h2 id="monday"&gt;Monday&lt;/h2&gt;
&lt;h3 id="folk-fomo"&gt;Folk FOMO&lt;/h3&gt;
&lt;p&gt;Whitby Folk Festival FOMO is real. But also, there&amp;rsquo;s a COVID spike going on, and crowded rooms full of unmasked singers aren&amp;rsquo;t the safest of environments, so I think I&amp;rsquo;ll comfort myself in the knowledge that at least I won&amp;rsquo;t be likely to bring an infection home with me.&lt;/p&gt;
&lt;h3 id="blog-fiddling"&gt;Blog fiddling&lt;/h3&gt;
&lt;p&gt;I swear I&amp;rsquo;m going to wrap my head around the workings of the way to optionally build a custom formatted Date tree using &lt;code&gt;org-capture&lt;/code&gt;, but for now I&amp;rsquo;ve just tweaked the template I use to add&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-org" data-lang="org"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cs"&gt;#+hugo&lt;/span&gt;&lt;span class="c"&gt;: more&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;to the heading for the week. This means that, when Hugo&amp;rsquo;s rendering the index page, the week&amp;rsquo;s notes will be represented by a summary which links to the extended per-day notes for the week.&lt;/p&gt;
&lt;p&gt;Furthermore, I&amp;rsquo;ve added a new capture to let me capture a weeks&amp;rsquo; summary. I think I&amp;rsquo;ll probably end up wrapping that in a &lt;code&gt;summarize-week&lt;/code&gt; command that will show me the wider weeknote context while I write the note, then mark the week as &lt;code&gt;DONE&lt;/code&gt;, then bring up &lt;code&gt;magit&lt;/code&gt; so I can commit and push the changes. But maybe not for a while yet, on the &amp;ldquo;fake it until you can&amp;rsquo;t stand &lt;em&gt;not&lt;/em&gt; to automate it.&amp;rdquo; principle.&lt;/p&gt;
&lt;h2 id="thursday"&gt;Thursday&lt;/h2&gt;
&lt;p&gt;So, I have the ADHD thing of putting a thing down and completely forgetting where I put it, or even its very existence. Object permanence is clearly not a thing with me.&lt;/p&gt;
&lt;p&gt;Or I thought it was.&lt;/p&gt;
&lt;p&gt;We have a house guest right now, and she has this habit of trying to help by tidying putting stuff in &amp;lsquo;sensible&amp;rsquo; places. So I&amp;rsquo;ll find the squash in amongst the bottles of oil, vinegar and sauces in a &lt;em&gt;completely other part of the kitchen;&lt;/em&gt; or Gill&amp;rsquo;s socks will show up in an admittedly convenient, but surprising, new place after I&amp;rsquo;ve given up looking for them in the place I usually put them, and the airer I dry them on, and in the washing machine and laundry basket. Maybe it&amp;rsquo;s on the floor between any of those places… Grrr.&lt;/p&gt;
&lt;p&gt;It turns out I&amp;rsquo;m kind of comfortable with not quite knowing where a thing is, but I am absolutely viscerally &lt;em&gt;infuriated&lt;/em&gt; by finding said thing in the wrong fucking place, somewhere I would never ever ever in a month of Sundays deliberately put it.&lt;/p&gt;
&lt;p&gt;And don&amp;rsquo;t… don&amp;rsquo;t get me started on the utter utter utter wrongness of using the lids of things as shelves. I will end up foaming at the mouth and shouting. Ask me how I know.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s all the more distressing because I hate getting angry about stuff, especially objectively trivial stuff like this, and so the rate spirals. Bah!&lt;/p&gt;
&lt;h2 id="saturday"&gt;Saturday&lt;/h2&gt;
&lt;p&gt;Had a great day at Mill Con 2 down in Peterborough. I still think filk music as a genre is a bit weird, but there&amp;rsquo;s no denying that the people who make it are lovely people, and it&amp;rsquo;s hard to beat singing in company for lifting your mood.&lt;/p&gt;
&lt;p&gt;Mike Whitaker was kind enough to give me a concert spot at a week&amp;rsquo;s notice too, so I did a forty minute set with a couple of songs with Loopy Pro in what was only the second time I&amp;rsquo;ve used the gear in any setting but my &amp;lsquo;streaming studio.&amp;rsquo; It went well, but there&amp;rsquo;s still a way larger profusion of wires than I&amp;rsquo;m happy with, and I definitely want to assemble some kind of all in one pedal setup if I&amp;rsquo;m going to be taking the gear out of the house more often.&lt;/p&gt;</content></entry><entry><title>Week ending 2023-08-20</title><link href="https://bofh.org.uk/2023/08/21/week-note/"/><id>https://bofh.org.uk/2023/08/21/week-note/</id><updated>2023-08-21T10:17:00Z</updated><summary>&lt;p&gt;A quiet week. Had to cancel Friday night&amp;rsquo;s singing session with Emily – initially because Emily&amp;rsquo;s still recovering from COVID, but on the night itself, my diabetes meds decided to give me hellacious indigestion. Ozempic/semaglutide might well do wonders for my HBA1c readings, but it can&amp;rsquo;t half mess with my guts as well.&lt;/p&gt;</summary><content type="html">&lt;p&gt;A quiet week. Had to cancel Friday night&amp;rsquo;s singing session with Emily – initially because Emily&amp;rsquo;s still recovering from COVID, but on the night itself, my diabetes meds decided to give me hellacious indigestion. Ozempic/semaglutide might well do wonders for my HBA1c readings, but it can&amp;rsquo;t half mess with my guts as well.&lt;/p&gt;
</content></entry><entry><title>Week ending 2023-08-13</title><link href="https://bofh.org.uk/2023/08/14/week-note/"/><id>https://bofh.org.uk/2023/08/14/week-note/</id><updated>2023-08-14T09:02:00Z</updated><summary>&lt;p&gt;Oops, missed a week; seems I didn&amp;rsquo;t have anything interesting to say, or I was too busy doing stuff to write about it. Probably the former.&lt;/p&gt;
&lt;p&gt;Not a bad week, this week. My step daughter and her family called in on their way back from holiday on Friday night and we spent a pleasant evening with them and a few Cawleys who were knocking about, sat outside the Wool Market. Mostly good food, but apparently the Greek place isn&amp;rsquo;t that good. Rustic Pizza is still good though.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Oops, missed a week; seems I didn&amp;rsquo;t have anything interesting to say, or I was too busy doing stuff to write about it. Probably the former.&lt;/p&gt;
&lt;p&gt;Not a bad week, this week. My step daughter and her family called in on their way back from holiday on Friday night and we spent a pleasant evening with them and a few Cawleys who were knocking about, sat outside the Wool Market. Mostly good food, but apparently the Greek place isn&amp;rsquo;t that good. Rustic Pizza is still good though.&lt;/p&gt;
&lt;h2 id="friday"&gt;Friday&lt;/h2&gt;
&lt;p&gt;Most of the proceeds of my &lt;em&gt;Magic: The Gathering&lt;/em&gt; cards will be spent on repairing my grandfather&amp;rsquo;s recliner. But&amp;hellip; I wouldn&amp;rsquo;t be me if I didn&amp;rsquo;t spend some of it on something gamelike. So I bought myself a &amp;lsquo;GameDad&amp;rsquo;. In my case, an &lt;a href="https://anbernic.com/products/rg353v-rg353vs"&gt;Anbernic RG 353VS&lt;/a&gt; and it&amp;rsquo;s a hell of a thing. Not much bigger than an old school Game Boy (and &lt;em&gt;cheaper!&lt;/em&gt; Not just in real terms, but the Game Boy launched at $89.99 and I got mine for $87.99), but with a large, bright colour screen and enough grunt to play SNES and PlayStation games at full tilt. Apparently, you can even make it play Nintendo 64 stuff, but not necessarily at full speed.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t really care about emulating consoles I never owned though. I want to play &lt;em&gt;Manic Miner&lt;/em&gt;, &lt;em&gt;Tempest&lt;/em&gt;, &lt;em&gt;Dig Dug&lt;/em&gt;, &lt;em&gt;Galaxians&lt;/em&gt; and the other games that gladly ate my pocket money, ten pence at a time, down the local arcade (the building&amp;rsquo;s still there in all it&amp;rsquo;s new brutalist concrete glory, but the arcade where I boggled at &lt;em&gt;I, Robot&lt;/em&gt; and thrilled to the exploits of the masters of &lt;em&gt;Defender&lt;/em&gt; and &lt;em&gt;Robotron&lt;/em&gt; is long gone).&lt;/p&gt;
&lt;p&gt;So, in search of that heady thrill and those unmistakable sound effects, I&amp;rsquo;ve been frequenting archive.org&amp;rsquo;s library of delights and installing a few of my old favourites.&lt;/p&gt;
&lt;p&gt;The first to get seriously played was my old favourite &lt;em&gt;Tempest&lt;/em&gt; &amp;ndash; Atari&amp;rsquo;s miracle of colour vector graphics where you controlled a spiky yellow thing running around the top of a blue tube shooting the terrifying geometric shapes that were climbing up towards you with deadly intent. When I first started playing it, I&amp;rsquo;d hold the fire button down and spin madly round the top of the tube and die all too quickly. But it was such fun I&amp;rsquo;d just shove another coin in the slot until my money was all gone. Then, in an arcade in Whitby, I watched someone playing the game in an entirely new way and my mind was blown. The walls weren&amp;rsquo;t blue! The colours were different and there were new, scary shapes. This guy wasn&amp;rsquo;t spinning around, and he wasn&amp;rsquo;t just holding down the fire button either.&lt;/p&gt;
&lt;p&gt;Tempest was unusual for the time in that it had autofire. If you held down the fire button on most games of the era, you&amp;rsquo;d fire one shot, then nothing would happen. But in Tempest, you&amp;rsquo;d autofire bursts of eight missiles, then a slight pause and the cycle would repeat. And it was the slight pause that would kill you. The Whitby guy had sussed that out and was mashing the fire button at a measured speed that kept up a constant stream of evenly spaced bullets that were far more likely to save you when a Flipper had reached the top of the tube and was making its way towards you; they could only kill you if they in the same space as you and were vulnerable to your shots while they were flipping that last step towards you. If you were simply relying on autofire, you could bet that that flip would happen during the short pause between bursts.&lt;/p&gt;
&lt;p&gt;Whitby guy had also worked out that the larger the angle a flipper had to flip through, the more chance you had of killing it before it killed you, so for lots of levels, it was just a matter of finding the safest place and staying there. There are a couple of levels where you were only &amp;lsquo;safe&amp;rsquo; from flippers coming from one side. Those were the levels that killed you unless you got good at moving from place of safety to place of safety.&lt;/p&gt;
&lt;p&gt;I watched intently and, when I returned to my home arcade, suddenly the top three scores &amp;ndash; the ones that got burned into non volatile memory &amp;ndash; on the arcade&amp;rsquo;s machine belonged to PDC. I could reliably reach the red levels and even the next, yellow, set.&lt;/p&gt;
&lt;p&gt;I can&amp;rsquo;t do that on the Game Dad. Not yet at least. I&amp;rsquo;m old enough and RSI&amp;rsquo;d enough, that the thought of bashing the fire button at 8 Hertz just gives me the shivers.&lt;/p&gt;
&lt;p&gt;But! Modern emulators have all sorts of convenience functions, surely I could configure something that would emulate the steady rate of fire that my youthful fingers were capable of. And maybe I could do something about the incredibly sensitive controls, where even the lightest touch of the analog stick would see me moving two or more segments when what I really wanted was a surgical one step move.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;I turns out that I could. But, frustratingly, not via the very slick UI. I had to edit text files! I had to make new text files. And because popping the Micro SD card out of the Game Dad and into a card reader, editing a file, putting it back in the GD, testing it and then having to fiddle with the text file again is… less than ideal, I did it by &lt;em&gt;logging into my handheld games console via SSH from my iPad, editing the file and just restarting the game!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You can&amp;rsquo;t do that with an original Game Boy can you? The damned thing&amp;rsquo;s running Linux. I&amp;rsquo;m at once annoyed that I had to log in to it and fiddle with text files and astonished that I could even do that.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s not the nostalgia that&amp;rsquo;s making me feel old, it&amp;rsquo;s my assumptions about what&amp;rsquo;s capable of what kind of computing.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Real Tempest machines have a gorgeously weighted &amp;lsquo;spinner&amp;rsquo; &amp;ndash; a free spinning rotary encoder with enough resolution that there still multiple &amp;lsquo;steps&amp;rsquo; per tube segment. It meant that your blaster had a lovely fluid movement with very direct control. Basically, Tempest is pretty damned close to perfect on its original hardware and if you ever get the chance to play on a well maintained machine, you should grab it!&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content></entry><entry><title>Week ending 2023-07-30</title><link href="https://bofh.org.uk/2023/07/30/week-note/"/><id>https://bofh.org.uk/2023/07/30/week-note/</id><updated>2023-07-30T22:48:00Z</updated><summary>&lt;p&gt;Three weeks on the trot. Definitely calling that a win.&lt;/p&gt;
&lt;p&gt;Also, Good Omens 2 is a delight. Still enough of Terry&amp;rsquo;s character hanging
around it, and the new writers help it not feel too Neil-y.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Three weeks on the trot. Definitely calling that a win.&lt;/p&gt;
&lt;p&gt;Also, Good Omens 2 is a delight. Still enough of Terry&amp;rsquo;s character hanging
around it, and the new writers help it not feel too Neil-y.&lt;/p&gt;
&lt;h2 id="wednesday"&gt;Wednesday&lt;/h2&gt;
&lt;p&gt;After a bit of fiddling, I&amp;rsquo;ve worked out how to add helpers to the Emacs `C-x 8` keymap, so now I have shortcuts for typing &amp;lsquo;λ&amp;rsquo;, &amp;lsquo;🙂&amp;rsquo; and various other characters that I type more or less frequently. Beats the crap out of doing `C-x 8 &amp;lt;ret&amp;gt;` and then typing out the name of the character I&amp;rsquo;m looking for.&lt;/p&gt;
&lt;p&gt;In case you&amp;rsquo;re interested, here&amp;rsquo;s the code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-emacs-lisp" data-lang="emacs-lisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;general-define-key&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;:keymaps&lt;/span&gt; &lt;span class="ss"&gt;&amp;#39;iso-transl-ctl-x-8-map&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;. ,&amp;#34;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;…&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;: )&amp;#34;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;🙂&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;: D&amp;#34;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;😀&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;; )&amp;#34;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;😉&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;\\&amp;#34;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;λ&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;a ^&amp;#34;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;↑&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;a u&amp;#34;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;↑&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;a v&amp;#34;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;↓&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;a d&amp;#34;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;↓&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;a |&amp;#34;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;↕&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you&amp;rsquo;re not using `general`, but you&amp;rsquo;ve got `use-package` installed, you can do something similar with `bind-keys`:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-emacs-lisp" data-lang="emacs-lisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;bind-keys&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;:map&lt;/span&gt; &lt;span class="ss"&gt;&amp;#39;iso-transl-ctl-x-8-map&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;. ,&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;…&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;: )&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;🙂&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;: D&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;😀&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;:|&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;😐&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;; )&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;😉&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;\\&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;λ&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;a ^&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;↑&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;a u&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;↑&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;a v&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;↓&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;a d&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;↓&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;a |&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;↕&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can no doubt use define-key as well, but I find `general` or `bind-keys` to be much nicer to work with. The latter has the advantage that it&amp;rsquo;s included in Emacs as part of `use-package` and plays nice with `which-key`, so I might go and redo my key bindings and get rid of `general`, nice as it is, since the real selling point of that library is how easy it is to bind stuff in `evil-mode` states.&lt;/p&gt;
&lt;h2 id="sunday"&gt;Sunday&lt;/h2&gt;
&lt;p&gt;I still miss &lt;em&gt;Twitch Sings.&lt;/em&gt; It&amp;rsquo;s how I started streaming—long before the Friday night Song Swaps and folk streams. I&amp;rsquo;d be happily belting out Lady Gaga&amp;rsquo;s &lt;em&gt;Bad Romance&lt;/em&gt;, hamming it up to &lt;em&gt;You Spin Me Round&lt;/em&gt; or giving it my best Johnny Cash
&lt;object role="note"&gt;&lt;p&gt;Not a particularly good impression. I can’t get that low!&lt;/p&gt;
&lt;/object&gt;
on &lt;em&gt;Hurt.&lt;/em&gt; It was just huge fun and a great way to make friends on Twitch.&lt;/p&gt;
&lt;p&gt;Twitch ended up pulling the plug because it was a free app and… well, free apps and sync rights really don&amp;rsquo;t play well together.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll still find people doing Karaoke on Twitch though, many of them the same faces I met back in &lt;em&gt;Twitch Sings&lt;/em&gt; days. This morning, I woke up early and spotted some friends Karaoke-ing it up on a Discord, so I pulled on pyjamas and went and joined &amp;rsquo;em for a few songs. These days, I just use &lt;a href="https://loopypro.com"&gt;Loopy Pro&lt;/a&gt; rather than searching YouTube for backing tracks. It&amp;rsquo;s great fun though, and definitely makes for a more enjoyable way to spend the occasional hour or so of early morning insomnia.&lt;/p&gt;
&lt;p&gt;Singing in company, even virtual company is still the best thing you can do in public with your clothes on. I encourage you all to sing more. What&amp;rsquo;s the worst that could happen?&lt;/p&gt;</content></entry><entry><title>Week ending 2023-07-23</title><link href="https://bofh.org.uk/2023/07/23/week-note/"/><id>https://bofh.org.uk/2023/07/23/week-note/</id><updated>2023-07-23T21:37:00Z</updated><summary>&lt;p&gt;Small victory of the week: Actually got off my arse and did something about selling off my old &lt;em&gt;Magic the Gathering&lt;/em&gt; cards. For my next trick, I hope to do the same with my collection of [mostly card] magic books.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Small victory of the week: Actually got off my arse and did something about selling off my old &lt;em&gt;Magic the Gathering&lt;/em&gt; cards. For my next trick, I hope to do the same with my collection of [mostly card] magic books.&lt;/p&gt;
&lt;h2 id="tuesday"&gt;Tuesday&lt;/h2&gt;
&lt;p&gt;Made a capture template for adding a week note. Support functions are currently not the prettiest, and don&amp;rsquo;t deal with a bunch of corner cases, but they seem to work for my case, so I&amp;rsquo;ll leave &amp;rsquo;em be for the time being. I plan to write it up in a longer post, and that will no doubt tweak my coder pride enough to make things suck a little less.&lt;/p&gt;
&lt;p&gt;Oh god, once I start fiddling with my Emacs configuration, it&amp;rsquo;s impossible to stop!&lt;/p&gt;
&lt;h2 id="wednesday"&gt;Wednesday&lt;/h2&gt;
&lt;p&gt;Nipped over to Mum and Dad&amp;rsquo;s for lunch at Zini&amp;rsquo;s, and to borrow dad&amp;rsquo;s drills for my on going cigar box MIDI controller project. Managed to get eight holes accurately placed enough that I only had to drill 7.8mm holes for the M7 threaded rotary encoders I&amp;rsquo;d soldered to my stripboard. I&amp;rsquo;m calling that a win! Next trick, get the microcontroller wired up and appropriate software written.&lt;/p&gt;
&lt;p&gt;Also discussed making PID controller I promised to make dad for his heat treatment setup a while back. A Pi Pico and one of its mini displays looks like it should do the job nicely. The plan is to make an extension cable with an SSR as a separate bit of kit, then control that from the prototype controller. Once they&amp;rsquo;re working as separate parts, we can work out how to bring it all into one container. I shall wuss out of making the kind of thing I saw in a commercial radio controlled plug, which powered the control circuit with a very simple capacitor based power supply, with the slightly worrying wrinkle that the controller&amp;rsquo;s 0V line was floating at around 5V below mains Vmax. Clever, sure, but scarier than I&amp;rsquo;m prepared to work with.&lt;/p&gt;
&lt;h2 id="friday"&gt;Friday&lt;/h2&gt;
&lt;p&gt;Holy crap, but old &lt;em&gt;Magic the Gathering&lt;/em&gt; cards are getting horrifically pricy. According to the buy list of the shop I just took my cards in to, I should be expecting about £400 for just four of my cards. And probably another couple of hundred for the two dual lands (assuming they&amp;rsquo;re not from the Unlimited set, in which case they&amp;rsquo;re worth a &lt;em&gt;lot&lt;/em&gt; more). All being well that&amp;rsquo;s covered the cost of getting my grandfather&amp;rsquo;s old recliner reupholstered and fixed.&lt;/p&gt;
&lt;p&gt;If I could be arsed with it, I could probably get a lot more by selling direct on eBay, but I was already losing the will to live just sorting things out to take in to the shop.&lt;/p&gt;
&lt;p&gt;Do &lt;em&gt;not&lt;/em&gt; ask me about the &lt;em&gt;Tabernacle at Pendrell Vale&lt;/em&gt; and &lt;em&gt;Black Lotus&lt;/em&gt; that I sold far too early, because that might make me grumpy.&lt;/p&gt;</content></entry><entry><title>Week ending 2023-07-16</title><link href="https://bofh.org.uk/2023/07/16/week-note/"/><id>https://bofh.org.uk/2023/07/16/week-note/</id><updated>2023-07-16T18:32:00Z</updated><summary>&lt;p&gt;Small victory of the week: I&amp;rsquo;m starting to get on top of the washing&lt;/p&gt;</summary><content type="html">&lt;p&gt;Small victory of the week: I&amp;rsquo;m starting to get on top of the washing&lt;/p&gt;
&lt;h2 id="tuesday"&gt;Tuesday&lt;/h2&gt;
&lt;p&gt;Overwhelm had left us with a huge pile of washing to do, filling the sink, the draining board, and various work surfaces and I kept putting off tackling it because my brainweasels just saw the sheer amount of work involved and shut down. No fun. Anyway, Gill grabbed her perching stool and set to and before I knew it there was a full dishwasher burbling away, an empty sink, a full draining board and the beginnings of a system to keep it that way. The goal is to keep the sink empty and the draining board full. Before I cook, I clear the draining board. Any pans I use go in the sink and the next time I make tea, I wash up what&amp;rsquo;s in the sink and anything that&amp;rsquo;s still not been done of the mahoosive pile, until there&amp;rsquo;s a full draining board again. Next time I&amp;rsquo;m brewing up, I can put the dry stuff away and, if I have the spoons, chip away at some more of the pile (though, post-COVID, I rarely have the spoons for much &amp;ndash; I can only stand for so long).&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s not perfect, but it gets stuff done, and I&amp;rsquo;m calling that a win.&lt;/p&gt;
&lt;h2 id="wednesday"&gt;Wednesday&lt;/h2&gt;
&lt;p&gt;Emily came over for rehearsal and Carcassonne. It was mostly Carcassonne, if I&amp;rsquo;m honest, but &lt;em&gt;The Housewife&amp;rsquo;s Lament&lt;/em&gt; is starting to seem like we&amp;rsquo;ve learned it, as is &lt;em&gt;We&amp;rsquo;ll Sit Upon The Gate,&lt;/em&gt; though that one feels like it could use another verse. &lt;em&gt;The Mary Ellen Carter&lt;/em&gt; is pretty damned solid too, which is good.&lt;/p&gt;
&lt;p&gt;We set a new record city score in Carcassonne too, managing to share a 105 point city. We even managed my first ever draw in the second game.&lt;/p&gt;
&lt;h2 id="friday"&gt;Friday&lt;/h2&gt;
&lt;p&gt;A solo stream this week. Again with the Overwhelm getting in the way of getting more guests booked, but I&amp;rsquo;m starting to fill the diary again, which is good. I&amp;rsquo;ve got Alex Cumming as my guest in a couple of weeks, and Helen Edwards, Talis Kimberley, Emily, and a folk legend who will remain nameless for the time being lined up for August and September.&lt;/p&gt;
&lt;p&gt;Loopy Pro was mostly rock solid. The one-shot overdub whine cropped up once, and there was a hard crash to the home screen at one point, but a restart was fast and clean. Perils of running beta versions of sofware, I guess.&lt;/p&gt;
&lt;h2 id="saturday"&gt;Saturday&lt;/h2&gt;
&lt;p&gt;I toddled over for the morning and afternoon sessions at the Bradfield Traditional Music Weekend &amp;ndash; spent a happy few hours singing Americana (I sang &lt;em&gt;Cabin in Glory&lt;/em&gt; and &lt;em&gt;We&amp;rsquo;re Gonna Camp a Little While in the Wilderness&lt;/em&gt; which seemed to go down well) in the first session, then there was a lovely, ballad heavy, song session. I sang &lt;em&gt;Tamlyn&lt;/em&gt; about as well as I&amp;rsquo;ve ever sung it, and it went down really well. I was knocked out by a cracking version of &lt;em&gt;The Famous Flower of Serving Men&lt;/em&gt; in particular, but the whole session was great.&lt;/p&gt;
&lt;h2 id="sunday"&gt;Sunday&lt;/h2&gt;
&lt;p&gt;Dim sum at the China Palace for lunch with Dougal &amp;amp; Liz, Matt and Jo and a few of the kids. Excellent as always. We may have overordered&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m off to the closing session of BTMW later. I suspect it will be as good as the Saturday sessions.&lt;/p&gt;</content></entry><entry><title>Impermanence matters</title><link href="https://bofh.org.uk/2023/06/06/impermanence-matters/"/><id>https://bofh.org.uk/2023/06/06/impermanence-matters/</id><updated>2023-06-06T10:43:00Z</updated><summary>&lt;p&gt;Back when lockdown started, for all we were both classed as &amp;lsquo;critically vulnerable&amp;rsquo;, we did pretty well. We had each other for company, the house is paid for, we have some private outdoor space, Doncaster Market remained open and I&amp;rsquo;d got a 25kg bag of flour. Life was… tolerable.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Back when lockdown started, for all we were both classed as &amp;lsquo;critically vulnerable&amp;rsquo;, we did pretty well. We had each other for company, the house is paid for, we have some private outdoor space, Doncaster Market remained open and I&amp;rsquo;d got a 25kg bag of flour. Life was… tolerable.&lt;/p&gt;
&lt;p&gt;Except, as any musician will tell you, making music needs company. At least it does if you catch the magic that happens when a group of people are together making something beautiful. Which is why I started singing folk songs to the internet&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; every Friday night.&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; At first, I just sang into the dark to the two or three people who showed up in the text chat &lt;object role="note"&gt;&lt;p&gt;Thank you all of you! You were awesome.&lt;/p&gt;
&lt;/object&gt;
but I was still missing harmonies. So I worked out how to use Logic for live sound and did on the fly multitracking of chorus harmonies and that was great. Recently, I&amp;rsquo;ve switched to using &lt;a href="https://apps.apple.com/gb/app/loopy-pro-looper-daw-sampler/id1492670451"&gt;Loopy Pro&lt;/a&gt; on my iPad&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt; and it&amp;rsquo;s been great &amp;ndash; I can sing harmonies on far more songs now and I&amp;rsquo;ve started experimenting with fancier arrangements too.&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class="newthought"&gt;A key realisation came&lt;/span&gt;
after a late night conversation with &lt;a href="https://johnspiers.co.uk/"&gt;Squeezy John&lt;/a&gt; [Spiers] on Twitter about how a YouTube archive of performances can become a millstone around an artist&amp;rsquo;s neck. After all, if someone can catch a performance online from a couple of years ago, why would they pay to watch you doing it again? I don&amp;rsquo;t think most people think this way, but the life of a professional musician is marginal enough that it doesn&amp;rsquo;t take many to have an effect. So I started making the catch-up videos private after a week. &lt;object role="note"&gt;&lt;p&gt;Or whenever I got round to it, if I’m honest&lt;/p&gt;
&lt;/object&gt;&lt;/p&gt;
&lt;p&gt;It was incredibly liberating. I stopped worrying too much about repeating myself every week &amp;ndash; I always open with the same song, I usually close with one of about three songs, there&amp;rsquo;s a couple of staples that people seem to miss if I don&amp;rsquo;t sing them and if I fuck something up, it&amp;rsquo;s gone in a week and I can concentrate on doing better next time. My audience seem to be fine with it. If I do something particularly well, or something funny happens like my cat bringing the backdrop down on me&lt;/p&gt;




 &lt;a href="https://youtu.be/kbeFRIqSUZg"&gt;https://youtu.be/kbeFRIqSUZg&lt;/a&gt;
 
 



&lt;p&gt;then I have the original video files, so I can edit and upload a high quality clip that I&amp;rsquo;m happy to leave available forever.&lt;/p&gt;
&lt;p&gt;And it&amp;rsquo;s closer to the experience of music in person, too. Live music is a magical transient thing that lasts as long as the song and then it&amp;rsquo;s gone. Recordings are souvenirs, they can be delightful, but they simply can&amp;rsquo;t be the same as being in the moment.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s gone. I might sing it again next week, but just as you can&amp;rsquo;t cross the same river twice, that performance will be different. And that&amp;rsquo;s brilliant.&lt;/p&gt;
&lt;p&gt;&lt;span class="newthought"&gt;I’ll tell you what is permanent:&lt;/span&gt;
the repeating Friday night entry in my diary that reads ‘Folk Stream.’ It doesn&amp;rsquo;t matter whether I&amp;rsquo;m streaming for 2 people and no tips or a busy room and £100+ in &lt;a href="https://ko-fi.com/pdcawley"&gt;Ko-fi&amp;rsquo;s hat&lt;/a&gt;, singing to the internet never fails to lift my mood. It&amp;rsquo;s like I&amp;rsquo;ve made an appointment with joy.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Catch me every Friday night at &lt;a href="https://youtube.com/pierscawley/live"&gt;https://youtube.com/pierscawley/live&lt;/a&gt; from 8pm UK Time.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;I tried a few of the Zoom singarounds that sprang up, but for reasons I can&amp;rsquo;t quite put my finger on, I found them far more stressful than just singing to the camer and interacting with an audience in text chat. The audio only Clubhouse ballad sessions that I started were way less stressy for me too.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;And a fancy dual-USB audio interface that means I can capture Loopy&amp;rsquo;s audio really cleanly. Check out the &lt;a href="https://www.amazon.co.uk/iConnectivity-AUDIO4c-Interface-Streaming-Performance/dp/B092L7TXL1?crid=2DB9Z9YW1UMFK&amp;amp;keywords=iconnectivity&amp;#43;audio4c&amp;amp;qid=1686042094&amp;amp;sprefix=iconnectivity&amp;#43;audio4c%2Caps%2C107&amp;amp;sr=8-5&amp;amp;linkCode=ll1&amp;amp;tag=justasummary-21&amp;amp;linkId=7d14e0bff14f201073ea18059f5abde5&amp;amp;language=en_GB&amp;amp;ref_=as_li_ss_tl"&gt;iConnectivity AUDIO4c&lt;/a&gt; (affiliate link) if that&amp;rsquo;s a thing you might need. There are other dual-USB audio interfaces that are probably at least as good, but this is the one I&amp;rsquo;ve got experience of using.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;Check out 



 &lt;a href="https://youtu.be/hWPmADRfPFQ"&gt;https://youtu.be/hWPmADRfPFQ&lt;/a&gt;
 
 


 for an example.&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</content></entry><entry><title>More Mastodon fiddling</title><link href="https://bofh.org.uk/2022/11/20/more-mastodon-fiddling/"/><id>https://bofh.org.uk/2022/11/20/more-mastodon-fiddling/</id><updated>2022-11-20T22:32:00Z</updated><summary>&lt;p&gt;There&amp;rsquo;s a certain frustrating joy in fiddling with the details of a thing so as to improve the formatting of the new thing you&amp;rsquo;ve just added to your site, and discovering that a side effect of the change is that a couple of niggles that you&amp;rsquo;d not quite got to the bottom of on the site itself are fixed in passing.&lt;/p&gt;
&lt;p&gt;I &lt;em&gt;think&lt;/em&gt; I&amp;rsquo;ve got the crossposting to Mastodon looking less awful, and I &lt;em&gt;know&lt;/em&gt; I&amp;rsquo;ve got &lt;a href="https://bofh.org.uk/"&gt;my front page&lt;/a&gt; looking better. Isn&amp;rsquo;t that lovely?&lt;/p&gt;</summary><content type="html">&lt;p&gt;There&amp;rsquo;s a certain frustrating joy in fiddling with the details of a thing so as to improve the formatting of the new thing you&amp;rsquo;ve just added to your site, and discovering that a side effect of the change is that a couple of niggles that you&amp;rsquo;d not quite got to the bottom of on the site itself are fixed in passing.&lt;/p&gt;
&lt;p&gt;I &lt;em&gt;think&lt;/em&gt; I&amp;rsquo;ve got the crossposting to Mastodon looking less awful, and I &lt;em&gt;know&lt;/em&gt; I&amp;rsquo;ve got &lt;a href="https://bofh.org.uk/"&gt;my front page&lt;/a&gt; looking better. Isn&amp;rsquo;t that lovely?&lt;/p&gt;</content></entry><entry><title>Migrating to Mastodon</title><link href="https://bofh.org.uk/2022/11/20/migrating-to-mastodon/"/><id>https://bofh.org.uk/2022/11/20/migrating-to-mastodon/</id><updated>2022-11-20T13:59:00Z</updated><summary>&lt;p&gt;Twitter, eh? Elon bloody Musk!&lt;/p&gt;
&lt;p&gt;In the happier timeline, Elton John bought Twitter and it became even more fabulous with every passing day. In the far more depressing timeline we find ourselves living in, Elon seems to be determined to tank the company and fuck the community.&lt;/p&gt;
&lt;p&gt;So I&amp;rsquo;ve buggered off to Mastodon. At the time of writing, you&amp;rsquo;ll find me at &lt;a href="https://mendeddrum.org/@pdcawley"&gt;@pdcawley@mendeddrum.org&lt;/a&gt; and, you might even be reading a version of this via Mastodon rather than directly on the site.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Twitter, eh? Elon bloody Musk!&lt;/p&gt;
&lt;p&gt;In the happier timeline, Elton John bought Twitter and it became even more fabulous with every passing day. In the far more depressing timeline we find ourselves living in, Elon seems to be determined to tank the company and fuck the community.&lt;/p&gt;
&lt;p&gt;So I&amp;rsquo;ve buggered off to Mastodon. At the time of writing, you&amp;rsquo;ll find me at &lt;a href="https://mendeddrum.org/@pdcawley"&gt;@pdcawley@mendeddrum.org&lt;/a&gt; and, you might even be reading a version of this via Mastodon rather than directly on the site.&lt;/p&gt;</content></entry><entry><title>General purpose computing is the best!</title><link href="https://bofh.org.uk/2022/05/12/general-purpose-computing-is-the-best/"/><id>https://bofh.org.uk/2022/05/12/general-purpose-computing-is-the-best/</id><updated>2022-05-12T13:21:00Z</updated><summary>&lt;p&gt;Sometimes folk ask why there&amp;rsquo;s a &lt;a href="https://raspberrypi.org/"&gt;Raspberry Pi&lt;/a&gt; in the rack case that holds my streaming rig and I admit that the primary reason is that I had one kicking about the place, and the rack unit that holds my M1 Mac Mini is designed to hold a couple of Pis in the space that isn&amp;rsquo;t holding the Mac, so I might as well attach it. I had the feeling it would come in handy.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Sometimes folk ask why there&amp;rsquo;s a &lt;a href="https://raspberrypi.org/"&gt;Raspberry Pi&lt;/a&gt; in the rack case that holds my streaming rig and I admit that the primary reason is that I had one kicking about the place, and the rack unit that holds my M1 Mac Mini is designed to hold a couple of Pis in the space that isn&amp;rsquo;t holding the Mac, so I might as well attach it. I had the feeling it would come in handy.&lt;/p&gt;
&lt;p&gt;It turns out, I was right. It runs OBS to handle motion graphics, and Companion to control the ATEM Mini from a streamdeck. In the future I plan to use it as a router so the gear in the rack can live on its own private network with the Pi handling the connection to the outside world either via a network cable or (hopefully not) wifi or a tethered phone.&lt;/p&gt;
&lt;p&gt;The latest &amp;ldquo;Oh, of course! It can do that too!&amp;rdquo; job it&amp;rsquo;s taken on is pretending to be an Apple TV, so I can mirror my iPad&amp;rsquo;s screen to it without mucking up the Mac&amp;rsquo;s display.&lt;/p&gt;
&lt;p&gt;Once again, I&amp;rsquo;m reminded that computers can be anything you can program them to be&amp;hellip; sometimes all at once. The Pi 4 is a mindbogglingly capable bit of kit. I&amp;rsquo;ve bought USB hubs&lt;object role="note"&gt;&lt;p&gt;USB Hubs that don’t work!&lt;/p&gt;
&lt;/object&gt;
that cost more and do less than my Pi.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a Pi 3 plugged in somewhere&lt;object role="note"&gt;&lt;p&gt;I think I know where it is, but I’m not entirely sure&lt;/p&gt;
&lt;/object&gt;
that runs &lt;a href="https://pi-hole.net"&gt;Pi Hole&lt;/a&gt; and helps eliminate intrusive ads from my web experience. It has sufficient spare power that, once I get around to it, I plan to hook up a cheap workhorse laser printer to it and configure it to work as a print server, which should let me retire the crappy unreliable bubblejet printer that only prints when you stand over it with a big stick and is always demanding the blood of innocents&lt;object role="note"&gt;&lt;p&gt;The printer calls it ink, but have you seen the price of that stuff?&lt;/p&gt;
&lt;/object&gt;
before it&amp;rsquo;ll deign to even try to print something.&lt;/p&gt;
&lt;p&gt;About the only thing wrong with them (at the moment) is that they&amp;rsquo;re almost permanently out of stock everywhere. I watch &lt;a href="https://rpilocator.com/"&gt;rpilocator&lt;/a&gt; and it&amp;rsquo;s clear that people do get stock, but they sell out almost immediately. A chum who works for Raspberry Pi says that they are shipping lots and lots of boards, but the demand stays sky high. Which is a nice problem to have for them, I suppose.&lt;/p&gt;
</content></entry><entry><title>Evolving my streaming setup</title><link href="https://bofh.org.uk/2022/05/10/evolving-my-streaming-setup/"/><id>https://bofh.org.uk/2022/05/10/evolving-my-streaming-setup/</id><updated>2022-05-10T17:10:00Z</updated><summary>&lt;p&gt;Back when I first started streaming on the internet, I used a Logitech webcam and some lights I had picked up for doing product photography and such for the Loafery and some audio gear I had because, well, recording stuff is just fascinating. It was okay, but even with decent lighting and audio, the webcam was frustrating to control (basically, there &lt;em&gt;was&lt;/em&gt; no control), so I picked up a cheap capture card from Amazon and drafted my Nikon D810&lt;object role="note"&gt;&lt;p&gt;Absolutely not the camera I would recommend if you’re going out to buy a camera for streaming – it’s primary virtue being that it’s the camera I already owned. Get something mirrorless if you’re heading down this road&lt;/p&gt;
&lt;/object&gt;
as my webcam and the appearance of my streams improved enormously. This worked fine with my slightly aging PC and &lt;em&gt;Twitch Sings&lt;/em&gt;.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Back when I first started streaming on the internet, I used a Logitech webcam and some lights I had picked up for doing product photography and such for the Loafery and some audio gear I had because, well, recording stuff is just fascinating. It was okay, but even with decent lighting and audio, the webcam was frustrating to control (basically, there &lt;em&gt;was&lt;/em&gt; no control), so I picked up a cheap capture card from Amazon and drafted my Nikon D810&lt;object role="note"&gt;&lt;p&gt;Absolutely not the camera I would recommend if you’re going out to buy a camera for streaming – it’s primary virtue being that it’s the camera I already owned. Get something mirrorless if you’re heading down this road&lt;/p&gt;
&lt;/object&gt;
as my webcam and the appearance of my streams improved enormously. This worked fine with my slightly aging PC and &lt;em&gt;Twitch Sings&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Then Twitch pulled the plug on &lt;em&gt;Sings&lt;/em&gt; and I started doing my regular Friday night folk streams. At first this involved running OBS and Logic Pro on my aging MacBook Pro and it was sort of okay, until I started having guests and doing Song Swaps &amp;ndash; the MacBook simply wasn&amp;rsquo;t up to the job of running Logic, OBS and Zoom, so for a while I had some unholy lashup, with Zoom and OBS running on the PC, my Mac running Logic, with patch leads between the Mac and PC audio interfaces so I could hear my guests and vice versa. It wasn&amp;rsquo;t pretty.&lt;/p&gt;
&lt;p&gt;What saved it was adding an &lt;a href="https://www.blackmagicdesign.com/uk/products/atemmini"&gt;ATEM Mini Pro ISO&lt;/a&gt; from BlackMagic. Now I could hand off all the capturing, streaming and recording duties to that box and run Logic and Zoom on the Mac and everything just worked (with an utter rats nest of cables on my desk). The ATEM takes HDMI input from up to four sources and lets me switch what gets sent to the stream between them. There&amp;rsquo;s also a monitor preview output that can be switched independently between those four sources, as well as multiview and stream previews. Of course, I quickly used up all four sources, and I don&amp;rsquo;t even have a second camera! The way things are arranged by default is that I have a camera feed, my mac&amp;rsquo;s primary and secondary displays, and the output from a Raspberry Pi. Mac screen 2 is where the zoom window lives, and is connected to the ATEM via an HDMI splitter with the second output feeding a 7-inch field monitor that sits on a teleprompter setup so I can look guests in the eye.&lt;object role="note"&gt;&lt;p&gt;Best purchase ever! I primarily use it with Zoom or to display the YouTube chat when I’m doing solo streams&lt;/p&gt;
&lt;/object&gt;&lt;/p&gt;
&lt;p&gt;The primary Mac screen is where I set up streams and drive Logic from. It feeds into the ATEM mostly so I can see it in the preview window or full size when I need to without having to have a another screen, but if I ever get a second camera, I&amp;rsquo;ll break mac screen 1 out to its own monitor and dedicate input 2 to that secondary camera. Input 4 is a Raspberry Pi that I use for motion graphics. This used to be a copy of chromium running in kiosk mode to display the &lt;a href="https://ko-fi.com/pdcawley"&gt;Ko-Fi&lt;/a&gt; stream widget which displays donations as they happen, but I&amp;rsquo;ve recently managed to compile OBS studio with a working browser plugin and I&amp;rsquo;m using that instead now, which should allow me to add more overlays later. The only catch with that is that I&amp;rsquo;ve not yet managed to get &lt;a href="https://bitfocus.io/companion/"&gt;Companion&lt;/a&gt; to compile, &lt;object role="note"&gt;&lt;p&gt;Companion’s a brilliant app for controlling AV gear via a web interface or Stream Deck. I know it works on the Pi because they provide OS images for it.&lt;/p&gt;
&lt;/object&gt;
so I think I&amp;rsquo;ll have to move that onto the Mac.&lt;/p&gt;
&lt;p&gt;&lt;span class="newthought"&gt;This all worked fine&lt;/span&gt;
until recently. You see, the thing I love about unaccompanied singing is singing harmonies. And harmonies don&amp;rsquo;t work unless the timing is properly tight. That means I can&amp;rsquo;t sing harmonies with Zoom guests because the speed of light&lt;object role="note"&gt;&lt;p&gt;and video and audio compression and decompression&lt;/p&gt;
&lt;/object&gt;
screws things up completely. I got around this by harmonising with myself &amp;ndash; either by using a Looper plugin in Logic&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; or just by using Logic&amp;rsquo;s multi-tracking&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; to record multiple layers of a song&amp;rsquo;s chorus. It all works, and works well, but I could never work out how to record harmonies for shanty type refrains.&lt;/p&gt;
&lt;p&gt;Consider:&lt;/p&gt;
&lt;div class="verse"&gt;
&lt;p&gt;Oh the rain it rains all day long&lt;br /&gt;
  &lt;em&gt;Bold Reilly oh, bold Reilly!&lt;/em&gt;&lt;br /&gt;
And them Northern winds they blow so strong&lt;br /&gt;
  &lt;em&gt;Bold Reilly oh&amp;rsquo;s gone away&lt;/em&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;strong&gt;Chorus:&lt;/strong&gt;&lt;br /&gt;
  &lt;em&gt;Goodbye my sweetheart, goodbye my dear-o&lt;/em&gt;&lt;br /&gt;
    &lt;em&gt;Bold Reilly oh, bold Reilly&lt;/em&gt;&lt;br /&gt;
  &lt;em&gt;Goodbye my darling, goodbye my dear-oh&lt;/em&gt;&lt;br /&gt;
    &lt;em&gt;Bold Reilly oh&amp;rsquo;s gone away&lt;/em&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Ideally, I want to be layering up harmonies on the &lt;em&gt;Bold Reilly oh, bold Reilly!&lt;/em&gt; and &lt;em&gt;Bold Reilly oh&amp;rsquo;s gone away&lt;/em&gt; lines within each verse, as well as on the chorus (and potentially on any chorus repeats too). It should be possible to set up Logic&amp;rsquo;s live looping feature to enable this sort of thing, but I could never work out how, and I wasn&amp;rsquo;t ready to switch to Ableton Live (where I couldn&amp;rsquo;t be sure I knew how to make it work either). So I stuck to just harmonising on the chorus and wishing there was a better way.&lt;/p&gt;
&lt;h2 id="enter-loopy-pro"&gt;Enter &lt;a href="https://loopypro.com/"&gt;Loopy Pro&lt;/a&gt;!&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ll write a full review of Loopy Pro one of these days, but suffice it to say, there is now a better way. Loopy Pro is the long awaited successor to Loopy HD, a software looper for iOS. It&amp;rsquo;s been a very long time coming, but by god, does it deliver! As well as being a looper, it&amp;rsquo;s a great replacement for MainStage and (at least for live use) Logic Pro, and it runs on even pretty ancient iPhones and iPads. It&amp;rsquo;s predecessor was remarkably capable, but was &amp;lsquo;just&amp;rsquo; a looper with a configurable number of loops available. Loopy Pro is fully customisable, supporting any number of loops, one shot samples, beat slicing, AUv3 hosting, mix busses, control widgets, faders and dials. There&amp;rsquo;s a deep system of actions and &amp;lsquo;follow actions&amp;rsquo; that allow you to customize its behaviour as well as its appearance, and the audio routing capabilities could embarrass some far more expensive DAW software. It&amp;rsquo;s astonishingly capable.&lt;/p&gt;
&lt;figure&gt;&lt;img src="/ox-hugo/shanty-view.png"
 alt="Figure 1: Loopy Pro configured for singing &amp;lsquo;shanty&amp;rsquo; structured chorus songs"&gt;&lt;figcaption&gt;
 &lt;p&gt;&lt;span class="figure-number"&gt;Figure 1: &lt;/span&gt;Loopy Pro configured for singing &amp;lsquo;shanty&amp;rsquo; structured chorus songs&lt;/p&gt;
 &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I&amp;rsquo;ve been fiddling with it since it was released, and now have it set up to allow me to sing shanty style songs with harmonies on the refrain as well as stuff like this:&lt;/p&gt;




 &lt;a href="https://youtu.be/hWPmADRfPFQ"&gt;https://youtu.be/hWPmADRfPFQ&lt;/a&gt;
 
 



&lt;p&gt;so now I&amp;rsquo;m running Friday night stream audio from the iPad, leaving the Mac to run Zoom. This has all been made &lt;em&gt;much&lt;/em&gt; easier since I added a new audio interface: the iConnectivity &lt;a href="https://www.iconnectivity.com/audio4c"&gt;AUDIO4c&lt;/a&gt; interface is a remarkable bit of kit. Uniquely, as far as I can tell, it can be used as an audio interface simultaneously by my mac and my iPad, and can route audio from the iPad to the mac and vice versa. It&amp;rsquo;s got four inputs and six outputs, which is two more than existing interface. That means I can have my guest and me fed to the ATEM mini on separate channels, as well as giving myself a different headphone mix. And it&amp;rsquo;s only one rack unit high!
&lt;object role="note"&gt;&lt;p&gt;Focusrite “small” interfaces, which are what I’ve used up to now are all about 1.5U high unless you buy the versions with 8 preamps – lovely bits of kit, but rather more than I either need or can afford.&lt;/p&gt;
&lt;/object&gt;
The remaining bit of the puzzle is to reliably capture the iPad display. I&amp;rsquo;m looking for, and so far failing to find, a powered USB-C hub that has a bulletproof iPad HDMI connection and gigabit ethernet, which is much more reliable when it comes to remote control of the ATEM. It&amp;rsquo;s a frustrating search. Everything I&amp;rsquo;ve found so far has intermittent HDMI dropouts, which would be annoying enough if it weren&amp;rsquo;t for another &amp;lsquo;feature&amp;rsquo; of iOS and iPadOS.&lt;/p&gt;
&lt;p&gt;&lt;span class="newthought"&gt;It works like this:&lt;/span&gt;
on iDevices, you don&amp;rsquo;t have the option to choose which audio interface you want to use, instead the OS autoselects whichever interface was plugged in most recently. Which would be fine (sort of) if it weren&amp;rsquo;t for the fact that an HDMI connection to an audio capable device is treated as a new audio interface. So&amp;hellip; if the HDMI connection was reliable, getting prepped for a stream would just involve unplugging the audio interface from the hub, connecting the HDMI and reconnecting to the AUDIO4c. But then the HDMI drops and comes back, and suddenly &lt;em&gt;it&lt;/em&gt; is the most recently connected interface and the stream&amp;rsquo;s audio is buggered.&lt;/p&gt;
&lt;p&gt;If you know of an iPad friendly USB-C hub that has rock solid HDMI, then I&amp;rsquo;d love to hear about it because my search is getting really frustrating. Right now I&amp;rsquo;m working around it by disconnecting the audio, screen sharing to the Mac and reconnecting the audio, but even with a dual screen mac set up, screen sharing takes over both screens&lt;object role="note"&gt;&lt;p&gt;Well… it takes over one screen and fades the other to black. Thanks Apple!&lt;/p&gt;
&lt;/object&gt;
and the whole thing has a bunch of latency that you don&amp;rsquo;t really want &amp;ndash; it&amp;rsquo;s surprising how little latency will start making audio software in particular seem seriously out of sync. I can correct for that when editing, but not so much on a live stream.&lt;/p&gt;
&lt;p&gt;But… once I have reliable HDMI from the iPad I run into another problem. Where do I plug it in? Suddenly four inputs on the ATEM aren&amp;rsquo;t really enough.&lt;/p&gt;
&lt;p&gt;Time to start saving up for an ATEM Mini Extreme ISO, which is the extra-wide version of the ATEM Mini Pro ISO, complete with 8 HDMI inputs, 2 HDMI outputs, 2 USB-C connections, a 3.5mm audio jack output (so I can monitor the audio going to the stream rather than just looking at the VU meters on the multiview display) and something called a &amp;lsquo;Super Source&amp;rsquo;, which would definitely simplify the business of setting up the split screen view when I have a Zoom guest.
&lt;object role="note"&gt;&lt;p&gt;Because of restrictions in the way the ATEM Mini Pro ISO works, I have to sit to one side of the shot because I can only adjust the scale, crop and position of one video source at a time. The Super Source deals with all that.&lt;/p&gt;
&lt;/object&gt;
The extreme version appears to fix pretty much everything that I find slightly annoying about the Mini, which is brilliant, but could be brillianter. Ah well, a boy can dream.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re interested in seeing what all this technology ends up looking like on stream, then I stream at 8pm UK time every Friday night on my &lt;a href="https://youtube.com/c/PiersCawley"&gt;YouTube channel&lt;/a&gt;. Maybe pop by, and if you like what you see and hear, don&amp;rsquo;t forget to like and subscribe.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;I used the delightfully named &lt;a href="https://www.expert-sleepers.co.uk/augustusloop.html"&gt;Augustus Loop&lt;/a&gt; from Expert Sleepers combined with a Lua script I wrote to make it behave more or less the way I wanted it to. Kind of fiddly to set up, but repaid the effort. There&amp;rsquo;s still a few things that AL can do that I can&amp;rsquo;t do with Loopy Pro, but as I write those are due in the next big Loopy patch.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Logic isn&amp;rsquo;t really set up to do live sound, but MainStage, which is, can&amp;rsquo;t do multi track recording and playback and I couldn&amp;rsquo;t work out how to configure its looper to emulate that. So I just used Logic.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content></entry></feed>