Author: Derrick

  • Command Timing in ZSH

    Command Timing in ZSH

    I’ve had a few snippets in my .zshrc file for a while now that will output how long a command takes to process.

    First off, I’d like to say that I did not come up with this idea, and I didn’t really write the code. I’ve snipped it from somewhere and modified it over time, so I am very sorry to the original author for not being able to give full credit. I didn’t save where I got it from–so if anyone comes across this in the future and might know where the idea came from, drop it in the comments.

    Now, on to the fun:

    The original script only went down to the second, but I wanted more granularity than that, so I went down to the millisecond.

    You’ll likely need to install gdate (brew install gdate) for this to work properly.

    The code (added to ~/.zshrc):

    function preexec() {
    	timer=$(($(gdate +%s%0N)/1000000))
    }
    
    function precmd() {
    	if [ "$timer" ]; then
    		now=$(($(gdate +%s%0N)/1000000))
    		elapsed=$now-$timer
    
    		reset_color=$'\e[00m'
    		RPROMPT="%F{cyan} $(converts "$elapsed") %{$reset_color%}"
    		export RPROMPT
    		unset timer
    	fi
    }
    
    converts() {
    	local t=$1
    
    	local d=$((t/1000/60/60/24))
    	local h=$((t/1000/60/60%24))
    	local m=$((t/100/60%60))
    	local s=$((t/1000%60))
    	local ms=$((t%1000))
    
    	if [[ $d -gt 0 ]]; then
    			echo -n " ${d}d"
    	fi
    	if [[ $h -gt 0 ]]; then
    			echo -n " ${h}h"
    	fi
    	if [[ $m -gt 0 ]]; then
    			echo -n " ${m}m"
    	fi
    	if [[ $s -gt 0 ]]; then
    		echo -n " ${s}s"
    	fi
    	if [[ $ms -gt 0 ]]; then
    		echo -n " ${ms}ms"
    	fi
    	echo
    }Code language: PHP (php)
  • Limiting Featured Image Dimensions in WordPress

    Limiting Featured Image Dimensions in WordPress

    As a follow up to my last post about limiting file sizes during uploads, I had to come back to the problem with limiting image sizes for featured images. Not bytes this time, but pixel dimensions.

    Still being a bit of a block editor newb, this was an interesting challenge for me, and I was really surprised at how easy it was to implement. I basically googled around and found a few different things to put together that worked for me. The primary source was a post by Igor Benic on how to disable the publish button:

    After that, I discovered how to add notifications to the block editor, to tell when the image was too large, thanks to a post by David F. Carr:

    So what’s it look like? Well, it’s pretty simple. If you choose a featured image that’s too large for the settings, it will add a non-dismissible error notification to the editor:

    Then it will block you from hitting the publish button:

    So, finally the code. This is just enqueued in as a simple add_action() during the enqueue_block_editor_assets hook. I’m absolutely not a good JS developer, so please don’t judge me too harshly. Also, that means this could be riddled with bugs. Use at your own risk 😀

    var postLocked = false;
    wp.domReady( () => {
    	wp.data.subscribe( function() {
    		var imageId = wp.data.select( 'core/editor' ).getEditedPostAttribute( 'featured_media' ); // Featured Image ID.
    
    		// If we have no image ID, and we already locked the post, we won't do anything.
    		if ( imageId ) {
    			blockEditorSettings = wp.data.select('core/block-editor').getSettings();
    
    			// Default to 1200px wide.
    			maxImageSize = 1200;
    			imageAttrs = wp.data.select('core').getMedia( imageId );
    
    			// Get the size for the "large image" and if it's available, use that instead.
    			if ( typeof blockEditorSettings !== 'undefined' && blockEditorSettings.hasOwnProperty( 'imageDimensions' ) ) {
    				maxImageSize = blockEditorSettings.imageDimensions.large.width;
    			}
    
    			if ( typeof imageAttrs !== 'undefined' && imageAttrs.hasOwnProperty( 'media_details') ) {
    				// Publish is not locked and width is too large.
    				if ( ! postLocked && imageAttrs.media_details.width > maxImageSize ) {
    					postLocked = true;
    					wp.data.dispatch( 'core/editor' ).lockPostSaving( 'featuredImageTooLarge' );
    
    					wp.data.dispatch('core/notices').createNotice(
    						'error', // Can be one of: success, info, warning, error.
    						wp.i18n.__( wp.i18n.sprintf( 'Featured image width must be less than %spx, currently %spx. Publishing is disabled.', maxImageSize, imageAttrs.media_details.width ) ),
    						{
    							id: 'featuredImageTooLarge', // Assigning an ID prevents the notice from being added repeatedly.
    							isDismissible: false, // Whether the user can dismiss the notice.
    						}
    					);
    				}
    
    			}
    		} else if ( postLocked ) {
    			postLocked = false;
    			wp.data.dispatch( 'core/editor' ).unlockPostSaving( 'featuredImageTooLarge' );
    			wp.data.dispatch('core/notices').removeNotice( 'featuredImageTooLarge' );
    		}
    	} );
    } );Code language: JavaScript (javascript)
  • Half Baked Idea: Limiting WordPress Image Upload Sizes

    Half Baked Idea: Limiting WordPress Image Upload Sizes

    If you want to be able to limit images (or any attachments) from being uploaded into the media library if they are too large, you can use the wp_handle_upload_prefilter filter to do this.

    Below is a really basic example I whipped up in a few minutes that I shared with a customer not too long ago:

    /**
     * Limits the maximum size of images that can be uploaded.
     *
     * @param array $file {
     *     Reference to a single element from `$_FILES`.
     *
     *     @type string $name     The original name of the file on the client machine.
     *     @type string $type     The mime type of the file, if the browser provided this information.
     *     @type string $tmp_name The temporary filename of the file in which the uploaded file was stored on the server.
     *     @type int    $size     The size, in bytes, of the uploaded file.
     *     @type int    $error    The error code associated with this file upload.
     * }
     *
     * @return array       Filtered file array, with a possible error if the file is too large.
     */
    function blarg_limit_max_image_upload_size( $file ) {
        if ( str_starts_with( $file['type'], 'image/' ) ) {
            $max_size = 1 * 1024; // 1kb.
    
            if ( $file['size'] > $max_size ) {
                // translators: %s: Human readable maximum image file size.
                $file['error'] .= sprintf( __( 'Uploaded images must be smaller than %s.' ), size_format( $max_size ) );
            }
        }
    
        return $file;
    
    }
    add_filter( 'wp_handle_upload_prefilter', 'blarg_limit_max_image_upload_size', 10, 1 );Code language: PHP (php)

    Good luck if you use any of my hot garbage in production 😀

  • Dall-E 2 Outpainting Experiment

    Dall-E 2 Outpainting Experiment

    OpenAI recently introduced a new feature called Outpainting to Dall-E 2

    https://x.com/OpenAI/status/1565009319447314432?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1565009319447314432%7Ctwgr%5Efed2d0e7c9337da1fb5e0ab9ec7bba462e57afe1%7Ctwcon%5Es1_c10&ref_url=https%3A%2F%2Fdataconomy.com%2F2022%2F09%2Fdall-e-2-launched-outpainting%2F

    This lets you easily expand your image without manual labor intensive hacks.

    With this, I decided to do a quick run through to see what’s on the other side of Bliss

    https://en.wikipedia.org/wiki/Bliss_(image)

    Now, I probably could have gotten something a lot better if I’d taken my time and adjusted my prompt, but this is good for a fun experiment:

    Feel free to download the raw file straight from DALL-E 2 if you’d like to do anything with it:

  • Stable Diffusion on M1 Macs!

    Stable Diffusion on M1 Macs!

    Run Stable Diffusion on your M1 Mac’s GPU

    Oh yes, this is going to be awesome!

    https://replicate.com/blog/run-stable-diffusion-on-m1-mac

    (via Hacker News)

  • Bad Hack: Restart Linux Server when memory is low 😬

    Bad Hack: Restart Linux Server when memory is low 😬

    I’m running something on my Raspberry Pi server that’s got a memory leak. I think it’s related to my Software Defined Radio (more on that another day), but I’m too lazy to actually track it down and fix it, so I’ve implemented the below hack to just restart my server when memory gets too low.

    #!/bin/bash
    FREE_SWAP=$(free | grep Swap | awk '{print $4/$2 * 100.0}')
    FREE_MEM=$(free | grep Mem | awk '{print $7/$2 * 100.0}')
    
    if [ -t 1 ]; then
            echo "Free mem: ${FREE_MEM}%, Free swap: ${FREE_SWAP}%"
    fi
    
    if [ 1 -gt "${FREE_SWAP%.*}" ]; then
            echo "[$(date)] SWAP IS TOO LOW! AT $FREE_SWAP, CHECKING MEM"
            if [ 5 -gt "${FREE_MEM%.*}" ]; then
                    echo "[$(date)] SWAP IS TOO LOW! AT $FREE_SWAP, MEM IS TOO LOW, AT $FREE_MEM -- RESTARTING"
                    reboot
            fi
    fi
    
    if [ 1 -gt "${FREE_MEM%.*}" ]; then
            echo "[$(date)] MEM IS TOO LOW! AT $FREE_MEM -- RESTARTING"
            reboot
    fiCode language: PHP (php)

    If the free swap memory gets below 1%, we check physical memory. If physical memory is below 5%, we reboot.

    We also do a general check on physical memory, and if it ever gets below 1%, we reboot.

    So far, this has been working out well. I’ve thrown it in a cron to run every five minutes:

    # Check memory every five minutes and reboot if too low
    */5 * * * * /usr/local/bin/memcheck >> /var/log/memcheck.logCode language: PHP (php)

    You can see, checking my log, the number of times it’s saved my butt from having to fix a frozen machine:

    $ cat /var/log/memcheck.log | ack RESTARTING
    [Mon Feb  7 03:20:02 UTC 2022] SWAP IS TOO LOW! AT 0, MEM IS TOO LOW, AT 4.44943 -- RESTARTING
    [Tue Feb 15 03:15:33 UTC 2022] SWAP IS TOO LOW! AT 0, MEM IS TOO LOW, AT 3.79613 -- RESTARTING
    [Mon Feb 21 14:30:01 UTC 2022] SWAP IS TOO LOW! AT 0, MEM IS TOO LOW, AT 2.31316 -- RESTARTING
    [Sun Feb 27 01:40:01 UTC 2022] SWAP IS TOO LOW! AT 0, MEM IS TOO LOW, AT 4.02053 -- RESTARTING
    [Mon Mar  7 00:05:01 UTC 2022] SWAP IS TOO LOW! AT 0, MEM IS TOO LOW, AT 4.95096 -- RESTARTING
    [Wed Mar  9 00:55:01 UTC 2022] SWAP IS TOO LOW! AT 0, MEM IS TOO LOW, AT 4.81816 -- RESTARTING
    [Wed Mar  9 17:50:02 UTC 2022] SWAP IS TOO LOW! AT 0, MEM IS TOO LOW, AT 4.72946 -- RESTARTING
    [Tue Mar 15 04:10:01 UTC 2022] SWAP IS TOO LOW! AT 0, MEM IS TOO LOW, AT 4.79014 -- RESTARTING
    [Sun Mar 20 16:20:01 UTC 2022] SWAP IS TOO LOW! AT 0, MEM IS TOO LOW, AT 4.95519 -- RESTARTING
    [Fri Mar 25 14:10:02 UTC 2022] SWAP IS TOO LOW! AT 0, MEM IS TOO LOW, AT 4.98074 -- RESTARTING
    [Tue Mar 29 15:05:01 UTC 2022] SWAP IS TOO LOW! AT 0, MEM IS TOO LOW, AT 1.63566 -- RESTARTING
    [Tue Apr  5 16:55:01 UTC 2022] SWAP IS TOO LOW! AT 0, MEM IS TOO LOW, AT 4.44242 -- RESTARTING
    [Fri Apr  8 01:40:55 UTC 2022] SWAP IS TOO LOW! AT 0, MEM IS TOO LOW, AT 1.23014 -- RESTARTING
    [Fri Apr  8 01:56:41 UTC 2022] SWAP IS TOO LOW! AT 0, MEM IS TOO LOW, AT 1.00451 -- RESTARTING
    [Mon Apr 11 03:15:02 UTC 2022] SWAP IS TOO LOW! AT 0, MEM IS TOO LOW, AT 3.76512 -- RESTARTING
    [Thu Apr 14 17:45:03 UTC 2022] SWAP IS TOO LOW! AT 0, MEM IS TOO LOW, AT 2.02634 -- RESTARTING
    [Wed Apr 27 17:15:02 UTC 2022] SWAP IS TOO LOW! AT 0, MEM IS TOO LOW, AT 4.55957 -- RESTARTING
    [Fri Apr 29 14:55:02 UTC 2022] SWAP IS TOO LOW! AT 0, MEM IS TOO LOW, AT 3.96232 -- RESTARTING
    [Mon May  2 11:00:02 UTC 2022] SWAP IS TOO LOW! AT 0, MEM IS TOO LOW, AT 2.62884 -- RESTARTING
    [Sat May  7 11:50:01 UTC 2022] SWAP IS TOO LOW! AT 0, MEM IS TOO LOW, AT 4.97909 -- RESTARTING
    [Wed May 11 08:00:02 UTC 2022] SWAP IS TOO LOW! AT 0, MEM IS TOO LOW, AT 4.83372 -- RESTARTING
    [Tue May 31 15:00:02 UTC 2022] SWAP IS TOO LOW! AT 0, MEM IS TOO LOW, AT 3.5021 -- RESTARTING
    [Thu Jun 16 00:20:02 UTC 2022] SWAP IS TOO LOW! AT 0, MEM IS TOO LOW, AT 2.98098 -- RESTARTING
    [Tue Jul  5 16:10:08 UTC 2022] SWAP IS TOO LOW! AT 0.0030518, MEM IS TOO LOW, AT 4.10944 -- RESTARTING
    [Tue Aug  9 04:10:20 UTC 2022] SWAP IS TOO LOW! AT 0, MEM IS TOO LOW, AT 3.11883 -- RESTARTING
    [Wed Aug 17 14:45:03 UTC 2022] SWAP IS TOO LOW! AT 0, MEM IS TOO LOW, AT 4.20299 -- RESTARTINGCode language: JavaScript (javascript)
  • How to Use Backticks for Inline Code in Google Docs with a Chrome Extension

    How to Use Backticks for Inline Code in Google Docs with a Chrome Extension

    https://chromewebstore.google.com/detail/backtick/gfollmknbahbmikbkhepbggabhdpjlhh

    Update from the extension author:

    Update Aug 11, 2023:

    About a year after this extension first came out, Google released built-in inline code markdown support for Google Docs, rendering this extension obsolete for most use-cases. The extension will remain for those want to continue using it, but I don’t intend on making any further updates. To get the built-in inline code and code blocks working via backticks, ensure the Tools -> Preferences -> “Automatically detect Markdown” checkbox is enabled inside Google Docs.


    Zach Brogan, you are my new best friend! I have to confess, I’m a big fan of Markdown. It’s an elegant and straightforward markup language that allows for easy formatting and structuring of text. Whether it’s writing blog posts, documentation, or even taking notes, Markdown is my go-to tool.

    One issue I’ve encountered, however, is that not all platforms fully support Markdown. For instance, when working in Google Docs, despite the presence of a “markdown” option, inline code blocks never seemed to work as expected. It was frustrating to say the least.

    That’s when I stumbled upon the “Backtick” Chrome extension, and it has been a game-changer for me. The extension seamlessly integrates into the Google Docs editor, allowing me to effortlessly insert inline code blocks using backticks. No more struggling with wonky formatting or trying to find workarounds.

    With the Backtick extension, I can write and format my inline code snippets in Markdown within Google Docs. It has truly revolutionized my workflow and made my writing experience a lot smoother and more enjoyable.

    So, if you enjoy Markdown as much as I do, I highly recommend giving the “Backtick” Chrome extension a try. It will save you time and frustration, and let you focus on what you do best – creating great content. Happy writing!

    Thank you!

    https://backtick.zachbrogan.com
  • Fixing a broken ATOM Feed

    Fixing a broken ATOM Feed

    My city is not known for being technologically adept, and I’m at least lucky they have a website with a CMS. Sadly though, the website offers only a broken ATOM 1.0 feed, a standard that’s old enough to drink in some countries.

    Unfortunately, this doesn’t work with NewsBlur, so I had to sort to building a proxy that would parse the XML and output a JSON Feed.

    Through the power of Phpfastcache (only for a little bit of caching), I am embarassed to show you this cobbled together mess:

    <?php
    define( 'DEBUG', false );
    
    if ( defined( 'DEBUG') && DEBUG ) {
    	ini_set('display_errors', 1);
    	ini_set('display_startup_errors', 1);
    	error_reporting(E_ALL);
    }
    
    use Phpfastcache\Helper\Psr16Adapter;
    require 'vendor/autoload.php';
    
    $cache     = new Psr16Adapter( 'Files' );
    $url       = 'https://www.cityoflinton.com/egov/api/request.egov?request=feed;dateformat=%25B%20%25d%20at%20%25X%23%23%25b%2B%2B%25d;featured=3;title=Upcoming%20Events;ctype=1;order=revdate';
    $cache_key = 'atom-feed_' . md5( $url );
    
    // Get and/or fill the cache.
    if ( ! $cache->has( $cache_key ) ) {
    	$atom_feed = file_get_contents( $url );
    	$cache->set( $cache_key, $atom_feed, 60 * 60 * 1 ); // 1 hour.
    } else {
    	$atom_feed = $cache->get( $cache_key );
    }
    
    $feed_data = new SimpleXMLElement( $atom_feed );
    
    $json_feed = array(
    	'version'       => 'https://jsonfeed.org/version/1.1',
    	'title'         => filter_var( trim( $feed_data->title ) ?? 'Upcoming Events for the City of Linton', FILTER_SANITIZE_STRING ),
    	'home_page_url' => 'https://www.cityoflinton.com/',
    	'feed_url'      => 'https://decarbonated.org/tools/cityoflinton-rss/',
    	'language'      => 'en-US',
    	'items'         => array(),
    );
    
    foreach( $feed_data->entry as $entry ) {
    	$json_feed['items'][] = array(
    		'id'            => md5( $entry->id ),
    		'url'           => filter_var( trim( $entry->link['href'] ) ?? 'NO LINK FOUND', FILTER_SANITIZE_URL ),
    		'title'         => filter_var( trim( $entry->title ) ?? 'NO TITLE FOUND', FILTER_SANITIZE_STRING ),
    		'content_text'  => filter_var( trim( $entry->summary ) ?? 'NO CONTENT FOUND', FILTER_SANITIZE_STRING ),
    		'date_modified' => ( new DateTime( trim( $entry->updated ) ?? now(), new DateTimeZone( 'America/New_York' ) ) )->format( DateTimeInterface::RFC3339 ),
    	);
    }
    
    if ( defined( 'DEBUG ' ) && DEBUG ) {
    	header( 'Content-Type: text/plain' );
    	var_dump( $json_feed );
    } else {
    	header( 'Content-Type: application/feed+json' );
    	echo json_encode( $json_feed );
    }
    Code language: HTML, XML (xml)

    This will convert the XML from (prettified):

    <?xml version="1.0" encoding="ISO-8859-1"?>
    <feed xmlns="http://www.w3.org/2005/Atom">
    	<title>Upcoming Events</title>
    	<link rel="self" href="https://www.cityoflinton.com/egov/api/request.egov?request=feed;dateformat=%25B%20%25d%20at%20%25X%23%23%25b%2B%2B%25d;featured=3;title=Upcoming%20Events;ctype=1;order=revdate" />
    	<updated>2021-12-09T10:14:40</updated>
    	<id>https://www.cityoflinton.com/egov/api/request.egov?request=feed;dateformat=%25B%20%25d%20at%20%25X%23%23%25b%2B%2B%25d;featured=3;title=Upcoming%20Events;ctype=1;order=revdate</id>
    	<author>
    		<name>Organization Information</name>
    	</author>
    	<entry>
    		<title>City Hall Closed</title>
    		<link rel="alternate" href="https://www.cityoflinton.com/egov/apps/events/calendar.egov?view=detail;id=501" />
    		<updated>2021-12-09T10:14:40</updated>
    		<id>https://www.cityoflinton.com/egov/apps/events/calendar.egov?view=detail;id=501</id>
    		<featured>0</featured>
    		<summary type="html">City Hall Closed</summary>
    	</entry>
    </feed>Code language: HTML, XML (xml)

    to JSON like:

    {
      "version": "https://jsonfeed.org/version/1.1",
      "title": "Upcoming Events",
      "home_page_url": "https://www.cityoflinton.com/",
      "feed_url": "https://decarbonated.org/tools/cityoflinton-rss/",
      "language": "en-US",
      "items": [
        {
          "id": "9b0bcc229cdc4266e539a785d77b4a8f",
          "url": "https://www.cityoflinton.com/egov/apps/events/calendar.egov?view=detail;id=501",
          "title": "City Hall Closed",
          "content_text": "City Hall Closed",
          "date_modified": "2021-12-09T10:14:40-05:00"
        }
      ]
    }Code language: JSON / JSON with Comments (json)

    That’s all ¯\_(ツ)_/¯

  • Quick Tip: Bash CLI params

    Quick Tip: Bash CLI params

    While working on a bash script, I stumbled upon what I think may be the cleanest and simplest way to add CLI params to a bash script so far:

    https://stackoverflow.com/questions/192249/how-do-i-parse-command-line-arguments-in-bash/14203146#14203146

    This lets you use short (-V), long (--version), space separated (--user john), and equal separated (--user=john) arguments.

    It’s not perfect, but for a quick bash script hack, I’ve found it very useful!