🌱 Seed Propagator

Climate and Eco

Home Page

Welcome to the digital garden of James Ravenscroft. This site is where I keep my notes in progress, some of the material is not fully fleshed out and some of the fleshed out work may be a bit rough around the edges. More polished work can be found on my main blog site.

Topics:

☕ Coffee

Good UK Coffee Merchants

Hayling Island Coffee Society (HICS), Portsmouth

A local (to me), independent coffee roaster on Hayling Island just north of Portsmouth, Hampshire

Notable Blend: HICS Volcano Island - a strong dark roast similar to Taylors Hot Java Lava

Wonky Coffee Company, London/Online

An online retailer selling coffee salvaged from coffee shops + houses in London. They sell beans and aluminium pods (the latter of which can be recycled with a Dualit ecopress)

The Mecca, Aberystwyth, Wales

An independent Tea & Coffee trader in Aberystwyth, Wales where I did my undergrad degree.

Music

I have a very ecclectic and general interest in music, from jazz to rap metal and from synthwave to cuban folk music.

Personal Stats

Whilst I still scrobble to Last.FM, I also maintain my own maloja instance

For Coding and Concentrating

I'm a massive fan of the Simcity 4 soundtrack by Jerry Martin and I've been on a yearslong quest to find similar music. Some of the recommendations in this reddit thread [mirror] are reasonably good.

One of my favourite tracks from the SimCity 4 soundtrack is EpiCenter by The Humble Brothers which is not available on the official soundtrack via spotify but various bootleg versions of it can be found on youtube. 

Recommendations

Pop/Rock

The Last Dinner Party - 2020s, Modern Rock

an all female rock band who made big waves in 2023. I first encountered their breakout hit Nothing Matters via spotify's AI dj and I'm glad I did, they are probably the most exciting modern band that I've encountered in about 10 years (the last being Haim whose debut album was the sound of my 2013). Every song they've released so far (at the time of writing - Dec 2023) has been absolute gold. I think My Lady of Mercy and Sinner are in tight contention for second and third favourite.

Funk and Soul

Jungle - 2010s and 2020s, neo-soul, pop

Although Jungle are probably tangential to mainstream at this point, they've been around for a while and you've more than likely heard their music in adverts or as background in restaurants. I've been following Jungle since a friend shared them on instagram around 2014. I am a huge fan of their blend of modern dancy sound with a funk/soul influence.As an entrypoint I highly recommend Keep Moving (if you don't do slow intros, skip to 1m30s in the youtube link) which has a very clear disco funk influence. When I saw them in Brighton in 2022 they even gave a nod to their disco influence by playing a couple of choruses of the Bee Gees "Stayin' Alive" during an extended jam. I would also recommend their first album in its entirety but, if pressed, Time is an excellent runner up point for getting into this band.

Dirty Loops - Swedish Funk Outfit

Dirty Loops are a trio of incredibly talented musicians who take leaves out of the books of funk, disco and pop greats. In particular, I have to recommend their Follow the Light album which they recorded in collaboration with Vulfpecker and funk guitar extraordinaire, Cory Wong. The titular song on the album is uplifting and fun and funky and Jonah channels Michael Jackson in the best way possible. In fact, in a nod to his sound-a-like, Jonah leads the vocals on an excellent cover of Thriller later on that album. Henrik's bass-lines are cool and crisp

Jazz and Jazz Fusion

Synthwave

Carpenter Brut - Synthwave, 80s Horror Aesthetic

One of the more mainstream Synthwave acts but one of the first acts that really lured me into the genre. I remember listening to Roller Mobster off the EPII album and being captivated by the wall of sound that hit me. It was like someone took the backing tracks to films from my childhood and turned them up to 11. I think it is the unique blend of 80s style with modern sound engineering techniques that gives it that effect. (NB: EPI, EPII and EPIII have since been re-released as "The Trilogy"). CB's subsequent albums have also been pretty solid offerings. I'm a big fan of Beware The Beast with Mat McNerney's vocals adding a certain je nais se quois to the whole track.

Jonny Fallout - Synthwave, Cyberpunk

I found Jonny Fallout by chance on Mastodon - he was giving away free downloads for one of his albums on bandcamp. He tends to mix his sound up - managing to land somewhere between between gentle chilled music "Night Drive" music and more aggressive fast-paced melodies that you could run or jog to. The titular track on "Awake in a Garden of Stars" is a great example of this kind of fusion.

Personal Knowledge Management

Fediverse

I am a member over at Fosstodon.

New to the Fediverse?

This guide written by Axbom has got you covered: https://axbom.com/mastodon-guide/

Who should I follow?

https://fedi.directory/ is a really cool list of interesting accounts that you could follow.

What tools are there?

As this toot says there are a huge number of apps that work on the Fediverse:

#Friendica - better #Facebook
#Funkwhale - like #Spotify and #SoundCloud
#Lemmy - like #Reddit
#PixelFed and #PeerPx - better #Instagram
#PeerTube - like #YouTube and #Vimeo
#Mastodon and #Pleroma - better #Twitter
#BookWyrm and #Inventaire - like #Goodreads
#distbin - like #Pastebin
#MissKey - better #Twitter and #Facebook combined
#Nextcloud - better #Dropbox, #Onebox, #GoogleDrive
#Owncast - live streaming
#WriteAs and #Plume - #Medium and hosted #WordPress on steroids

BulletTrain - Handwritten BuJo Companion App

Building the App

It's a mobile app powered by react native. I'm collecting my thoughts on how to build stuff with react native here

Storage

Books can be synced to/from remote storage systems. I will target webdav first because that's my personal itch but I guess theoretically I could also allow syncing with Google, Dropbox and maybe even something like S3.

Handwriting Recognition

Looks like CRAFT + TrOCR is likely to yield the kind of thing we want for recognising handwriting

Bookmarks

Gaming

One of my hobbies is video gaming. I recently got my hands on a Steam Deck which I would describe as a "Nintendo Switch Pro". I've been very impressed with the capabilities of the system.

Watch List

Webmentions

Webmentions are a way for IndieWeb folks to notify each other that something has happened, they use microformats internally.

WebMention.App provides an API for sending web mentions automatically but you have to know which page you want to send them from. I will probably add a module to Microcosm to check pages and use webmention.app to send mentions.

Steam Deck

Proton

Use ProtonUp to install custom versions of proton on the deck. You can find this in the Software store on the deck's KDE desktop (open up Discover and search protonup-qt)

Heroic Game Launcher

Heroic is a GUI that wraps both Epic and GOG allowing installation of games from both stores. Install Heroic through discover. Heroic can be added as a non-steam game so that it can be used to launch games from other stores in entertainment mode.

Running Rockstar Launcher on SD

This article explains how to run Rockstar Launcher on Steam Deck

IndieWeb

I've been interested in IndieWeb since I encountered the concept and owning your own data for a long time. My own site Brainsteam uses micropub and microsub and can receive webmentions. I use my own hand-rolled micropub endpoint in combination with the Hugo static site generation framework to manage my web content.

Making your site Indieweb compliant

I recently discovered IndieWebify.me which provides a bunch of tools for validating that your site is indie-web ready. I use Micropub.Rocks to ensure that my publication endpoint is up to par.

Micropub & Sub Clients

Who is on the IndieWeb?

OpenGraph

Hypothes.is

Hypothesis is a web annotation tool - you can annotate any page and your comments are then public for others to see (or you can privately annotate stuff)

Data Ownership

Hypothes.is is an open source project run by a non-profit. They consider all annotations made in public on their platform to be creative commons public domain so it seems like they're unlikely to try and do a runner with public data. However, I still have concerns leaving all my annotations in their silo (What if their service goes down and all my annotations disappear overnight?)

Annotating Archived Content

It's been pointed out in a couple of places that hypothes.is can be used to annotate data in the internet web archive but what about in my own archive?

Using via.hypothes.is with my own archivebox instance doesn't work but it seems that both the official chrome plugin and the unofficial firefox plugin do work with archivebox if you click through to the single page html you want to view before you activate them.

Active Hypothes.is Users

Ton Zylstra keeps a list of active and semi active h users

Interacting with annotations from other sources

Matthias 'x23' Melcher has built a drag and drop thinking/note mapping tool which he calls Condensr. Condensr has import and export functionality from a number of sources including hypothes.is, Kindle and Zotero.

The easiest way to get hypothes.is annotations into Condensr is via Jon Udell's export tool

Plumbing

odds and sods about plumbing maintenance

Plumbing

Water Hammer

Water hammer is the noise/shockwave generated when water is suddenly stopped/cut off.

Over time it can damage your system doe to the shockwaves along the pipes.

 

Source

https://piperepair.co.uk/2020/07/06/water-hammer-what-it-is-and-how-you-can-stop-it/ (mirror)

Plumbing

Power Flush

A procedure during which your central heating system is drained and cleaning solution is pumped around to displace and sludge which has been generated by corrosion within the system. The procedure should cost between £300-£500 to be done as of Q4 2022. A GasSafe engineer is required to carry out this maintenance procedure in the UK.

 

Source

https://www.diyplumbing.co.uk/power-flush-what-is-it-why-do-i-need-it-how-much-is-it/ (mirror)

Free Open Source Software and Open Culture

Free Open Source Software and Open Culture

Delightful Software

A curated list of FOSS software that is delightful

https://delightful.club/

Flipping the Bozo Bit

Flipping the bozo bit on someone is a mental trap/antipattern/shortcut whereby we assume the person is a bozo. This could mean that you assume all of their opinions are useless going forward and/or that you treat them like an idiot.

This is an expression I first encountered on Scott Hanselman's Hanselminutes podcast episode with Kris Nova in which they discuss fostering an atmosphere of psychological safety at work. Hanselman and Nova give the example of "there's no such thing as a stupid question" as a place where one might fall into this trap. You should assume that people don't know stuff that might seem obvious to you and that that's ok. We can't know everything. Preventing people from asking stupid questions can result in them having imposter syndrome.

There is an extensive page on this phenomenon over at the c2 wiki which suggests that the usage of the term bozo bit in this context was originally coined by Jim McCarthy in Dynamics of Software Development. It is thought to originate from a copy protection system in early macOS.

Proton Mail Bridge

Proton Mail Bridge is a tool that allows end users of Proton Mail to send and receive email using traditional protocols (i.e. SMTP and IMAP).

It can be installed and run on a desktop machine but there is an unofficial docker image which you can use to run it as a service for applications that need to send automated emails.

In order to use it I created a simple Docker Compose file:

version: "2.0"
services:
    bridge:
        image: shenxn/protonmail-bridge:latest
        restart: unless-stopped
        volumes:
            - /mnt/user/Docker/protonbridge/data:/root
        ports:
            - 1025:25
            - 1143:143
  1. On first run I did docker-compose run -i bridge init which displays a terminal.
  2. Enter login and use your proton login info
  3. now enter info and you should be given some credentials for logging in to SMTP and IMAP locally. You can use these for web apps that need to send emails or devices on the same network as your docker host that need IMAP access. Remember to replace the IP address 127.0.0.1 with whatever your docker host's address is.

TLS Certificate Warnings


You will probably find that downstream applications complain about the TLS certificates on your bridge server being funny when you try to connect. Certificate verification is very useful and should not be turned off if in doubt. I am using both the bridge and the things connecting to the bridge are on my home LAN and I am reasonably confident that there is very limited risk of a man-in-the-middle attack from inside the network so I've turned off TLS verification. If you're connecting to a bridge over the open net I would strongly recommend doing this properly (get the certificates from the vault and add it to your application's trust store properly).

Extracting Video Transcripts from MS Teams

Sometimes when you're using Teams you want to extract the full video auto-transcript so that you can summarize it or something but it's difficult or impossible to extract the full transcription unless the original author permits you to "Download" it.

Not a problem with this user-script for extracting video transcripts.

Instructions

  1. Install TamperMonkey in Firefox or Chrome
  2. Open the extension and click "create new script"


3. Paste the following Script into the editor and click Save
// ==UserScript==
// @name         SharePoint Video Transcript Extractor
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Extract transcript data from SharePoint video pages
// @author       You
// @match        *://*.sharepoint.com/*stream.aspx*
// @match        *://*.sharepoint.com/*/stream.aspx*
// @match        *://*.sharepoint.com/*/*/stream.aspx*
// @grant        GM_setClipboard
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';

    // Create a floating button to trigger the extraction
    const extractButton = document.createElement('button');
    extractButton.textContent = 'Extract Transcript';
    extractButton.style.position = 'fixed';
    extractButton.style.top = '10px';
    extractButton.style.right = '10px';
    extractButton.style.zIndex = '9999';
    extractButton.style.padding = '10px';
    extractButton.style.backgroundColor = '#0078d4';
    extractButton.style.color = 'white';
    extractButton.style.border = 'none';
    extractButton.style.borderRadius = '4px';
    extractButton.style.cursor = 'pointer';

    document.body.appendChild(extractButton);

    extractButton.addEventListener('click', extractTranscript);

    async function extractTranscript() {
        // Show extraction in progress
        extractButton.textContent = 'Extracting...';
        extractButton.disabled = true;

        // Find the main list container
        const listContainer = document.querySelector('.ms-List');

        if (!listContainer) {
            alert('Transcript list not found on this page.');
            extractButton.textContent = 'Extract Transcript';
            extractButton.disabled = false;
            return;
        }

        // Store extracted transcript data
        const transcriptData = [];
        let processedIds = new Set(); // Track processed entries by ID to avoid duplicates
        let noNewContentCounter = 0;
        let lastCellCount = 0;

        // Keep scrolling and extracting until no new content loads
        while (noNewContentCounter < 3) { // Try a few times to ensure we've reached the end
            // Extract current visible cells
            const currentCells = Array.from(document.querySelectorAll('.ms-List-cell'));

            if (currentCells.length === lastCellCount) {
                noNewContentCounter++;
            } else {
                noNewContentCounter = 0;
                lastCellCount = currentCells.length;
            }

            // Process all visible cells
            for (const cell of currentCells) {
                try {
                    // Get the list item ID to avoid duplicates
                    const listItemElement = cell.querySelector('[id^="listItem-"]');
                    if (!listItemElement) continue;

                    const itemId = listItemElement.id;
                    if (processedIds.has(itemId)) continue;
                    processedIds.add(itemId);

                    // Extract speaker name - look for the itemDisplayName element
                    let speakerElement = cell.querySelector('[class*="itemDisplayName"]');
                    let speaker = speakerElement ? speakerElement.textContent.trim() : '';

                    // If no speaker found, this might be a continuation from previous speaker
                    if (!speaker) {
                        // Try to get speaker from aria label
                        const ariaLabelElement = cell.querySelector('[id^="timestampSpeakerAriaLabel-"]');
                        if (ariaLabelElement) {
                            speaker = ariaLabelElement.textContent.trim().split(' ').slice(0, -3).join(' ');
                        }
                    }

                    if (!speaker) speaker = 'Unknown Speaker';

                    // Extract timestamp - look for timestamp element
                    let timestampElement = cell.querySelector('[id^="Header-timestamp-"]');
                    if (!timestampElement) {
                        // Try alternative timestamp element
                        timestampElement = cell.querySelector('[class*="baseTimestamp"] [aria-hidden="true"]');
                    }
                    const timestamp = timestampElement ? timestampElement.textContent.trim() : '';

                    // Extract text content - look for entry text element
                    const textElement = cell.querySelector('[class*="entryText"]');
                    const text = textElement ? textElement.textContent.trim() : '';

                    if (text && !text.includes('started transcription')) {
                        transcriptData.push({
                            speaker,
                            timestamp,
                            text
                        });
                    }
                } catch (error) {
                    console.error('Error processing cell:', error);
                }
            }

            // Scroll down to load more content
            const lastCell = currentCells[currentCells.length - 1];
            if (lastCell) {
                lastCell.scrollIntoView({ behavior: 'smooth', block: 'end' });
                // Wait for potential new content to load
                await new Promise(resolve => setTimeout(resolve, 1500));
            } else {
                break;
            }
        }

        // Format the data as text
        let formattedText = '';
        transcriptData.forEach(item => {
            formattedText += `[${item.timestamp}] ${item.speaker}: ${item.text}\n\n`;
        });

        // Copy to clipboard
        GM_setClipboard(formattedText);

        // Notify user
        extractButton.textContent = 'Copied to Clipboard!';
        setTimeout(() => {
            extractButton.textContent = 'Extract Transcript';
            extractButton.disabled = false;
        }, 3000);

        // Also show a notification
        alert(`Transcript extraction complete!\n${transcriptData.length} entries copied to clipboard.`);
    }
})();
(function() {
    'use strict';

    // Your code here...
})();


4. Open the video - if you are inside Teams, click the Open In stream button in the top right to open it in browser


5. With the script enabled, you should see an extra button that says "Extract Transcript", click on it.


6. While the extraction is happening the button will say "Extracting..." you will see that the transcript box on the right starts scrolling on its own...



7. When it is finished it will pop up and tell you how many lines of dialogue it grabbed. It puts everything in your clipboard.



8. Paste your transcript into your favorite LLM and summarize away!