Category Archives: Pals

An Ode to the Dremel

It’s not the tool I use around the house the most. That’s probably the Skeletool I keep on my hip or my drill. I actually look forward to seeing how fast I can assemble flatpack furniture with my drill.

But the Dremel makes little impossible things possible in a small apartment.

Like when the top for my daughter’s favorite water bottle broke.

It’s a very short task to use the carving attachments to cut the sharp bits off and then use a sander to round over the edges.

A water bottle covered in cute pandas and goat stickers. In front of it, the top is roughly rounded over and sanded down where it was damaged previously.

Little things become easy. Or if we need to cut off a section of our outdoor tiles or small metals sections. If you can’t fit it in tin snips, you can still use the metal cutting disks to cut through it or score things enough to snap.

Every Frame a Wallpaper

Years ago, Tony Zhou and Taylor Ramos made 28 video essays about film form called Every Frame a Painting and it’s incredible in teaching outsiders a whole new way to think about the art of film. The title is perfect. The content is just stunning, simple ways to look at masters of a form at work.

Lots of folks are known for one-shot takes, but this shows how Spielberg sneaks in gorgeous “oners” that do work without calling attention to themselves.

This essay on Fincher is great, but I love the little golden nugget about how spacing shows the evolving relationship between Mills and Somerset

That title always struck me. Every Frame a Painting. That’s gotta be a bar filmakers strive for. Some make it.

Some movies are just so damn beautiful. Just gorgeous.

Like Across the Spider-Verse. Yowza!

Like Sita Sings the Blues! Beautiful.

Like The Fountain

Like the one that you like that isn’t my cup of tea.

Might be nice to see an image from it, right there behind all of your terminals and windows and such, set as your wallpaper. If every frame’s a painting, set a random one as your wallpaper whenever I like it.

So here’s the plan. I want it. So I made it for me. You can have it. But here’s the terms of the deal. I made it for me, so if it doesn’t work for you, you have to make it work for you. If it causes you problems, those are not my problems. If you don’t agree, this isn’t for you.

This will take as an input a movie file, anything that ffmpeg can deal with. You’ll need to install ffmpeg – look on the official site for instructions.

By default, it won’t use the first 5 or last 10 minutes since that’s often the credits. But you can override this.

We’ll find out how many frames are in that remaining part of the movie.

We’ll pick one randomly and extract it from the movie.

Then we’ll set it as your wallpaper. Nice!

Want to change this often? Set up a cron job!

Pulling a single frame out the middle of a movie is CPU intense, so you probably want to use nice in your cron job so it doesn’t interfere with the rest of your work.

Here’s the code, save this in a file called every_frame_a_wallpaper.zsh and then chmod u+x every_frame_wallpaper.zsh

#! /bin/zsh
# This is a pretty processor intensive set of tasks! You should probably nice this script
# as in call it with nice -n 10 "every_frame_a_wallpaper.zsh /path/to/video.mkv"

SCRIPT_NAME=$(basename "$0")

# I like a nice log file for my cron jobs
function LOG() {
  echo -e "$(date --iso-8601=seconds): [$SCRIPT_NAME] :  $1"
}

# set up some options
local begin_skip_minutes=5
local end_skip_minutes=10
local wallpaper="$HOME/Pictures/wallpaper.png"
local usage=(
	"$SCRIPT_NAME [-h|--help]"
	"$SCRIPT_NAME [-b|--begin_skip_minutes] [-e|--end_skip_minutes] [<video file path>]"
	"Extract a single random frame from a movie and set it as wallpaper"
	"By default, skips 5 minutes from the beginning and 10 from the end, but this is overridable"

)

# the docs suck on zparseopts so let this be a reference for next time
# -D pulls parsed flags out of $@
# -F fails if we find a flag that wasn't defined
# -K allows us to set default values without zparseopts overwriting them
# Remember that the first dash is automatically handled, so long options are -opt, not --opt
zparseopts -D -F -K -- \
	{h,-help}=flag_help \
	{b,-begin_skip_minutes}:=begin_skip_minutes \
	{e,-end_skip_minutes}:=end_skip_minutes \
	|| return 1

[[ -z "$flag_help" ]] || {print -l $usage && return }
if [[ -z "$@" ]] {
   print -l "A video file path is required"
   print -l $usage && return

} else {
   MOVIE="$@"
}

if [[ $DISPLAY ]]
then
  LOG "interactively running, not in cron"
else
  LOG "Not running interactively, time to export the session's environment for cron"
  export $(xargs -0 -a "/proc/$(pgrep gnome-session -n -U $UID)/environ") 2>/dev/null
fi

LOG "skipping $begin_skip_minutes[-1] minutes from the beginning"
LOG "skipping $end_skip_minutes[-1] minutes from the end"
LOG "outputting the wallpaper to $wallpaper"
LOG "using file $MOVIE"

LOG "Let's get a frame from ${MOVIE}";


LOG "What's the duration of the movie?"
DURATION=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 \
  $MOVIE);
DURATION=$(printf '%.0f' $DURATION);

LOG "Duration looks like ${DURATION} seconds";

LOG "what's the frame rate?"

FRAMERATE=$(ffprobe -v error -select_streams v:0 \
  -show_entries \
  stream=r_frame_rate \
  -print_format default=nokey=1:noprint_wrappers=1 $MOVIE)




FRAMERATE=$(bc -l <<< "$FRAMERATE");

FRAMERATE=$(printf "%.0f" $FRAMERATE);

LOG "Looks like it's roughly $FRAMERATE"

FRAMECOUNT=$(bc -l <<< "${FRAMERATE} * ${DURATION}");
FRAMECOUNT=$(printf '%.0f' $FRAMECOUNT)
LOG "So the frame count should be ${FRAMECOUNT}";


SKIP_MINUTES=$begin_skip_minutes[-1]
SKIP_CREDITS_MINUTES=$end_skip_minutes[-1]

LOG "We want to skip $SKIP_MINUTES from the beginning and $SKIP_CREDITS_MINUTES from the end".

SKIP_BEGINNING_FRAMES=$(bc -l <<< "${FRAMERATE} * $SKIP_MINUTES * 60");
LOG "So $SKIP_MINUTES * 60 seconds * $FRAMERATE frames per second = $SKIP_BEGINNING_FRAMES frames to skip from the beginning."
SKIP_ENDING_FRAMES=$(bc -l <<< "${FRAMERATE} * $SKIP_CREDITS_MINUTES * 60");
LOG "So $SKIP_CREDITS_MINUTES * 60 seconds * $FRAMERATE frames per second = $SKIP_ENDING_FRAMES frames to skip from the ending."

USEABLE_FRAMES=$(bc -l <<< "$FRAMECOUNT - $SKIP_BEGINNING_FRAMES - $SKIP_ENDING_FRAMES");
UPPER_FRAME=$(bc -l <<<"$FRAMECOUNT - $SKIP_ENDING_FRAMES")
LOG "That leaves us with ${USEABLE_FRAMES} usable frames between $SKIP_BEGINNING_FRAMES and $UPPER_FRAME";

FRAME_NUMBER=$(shuf -i $SKIP_BEGINNING_FRAMES-$UPPER_FRAME -n 1)
LOG "Extract the random frame ${FRAME_NUMBER} to ${wallpaper}";
LOG "This takes a few minutes for large files.";
ffmpeg \
  -loglevel error \
  -hide_banner \
  -i $MOVIE \
  -vf "select=eq(n\,${FRAME_NUMBER})" \
  -vframes 1 \
  -y \
  $wallpaper


WALLPAPER_PATH="file://$(readlink -f $wallpaper)"
LOG "Set the out file as light and dark wallpaper - using ${WALLPAPER_PATH}";
gsettings set org.gnome.desktop.background picture-uri-dark "${WALLPAPER_PATH}";
gsettings set org.gnome.desktop.background picture-uri "${WALLPAPER_PATH}";

In my crontab I call it like this:

# generate a neat new background every morning
0 4 * * * nice -n 10 ~/crons/every_frame_a_wallpaper.zsh -b 5 -e 12 /home/mk/Videos/Movies/Spider-Man_Across_the_Spider-Verse.mkv >> ~/.logs/every_frame_a_wallpaper/`date +"\%F"`-run.log 2>&1

Moving on from GoodReads to BookWyrm

Friends, I’ve read a lot of books.

I started tracking books a long time ago on LibraryThing, when LibraryThing was giving out a CueCat. I liked LibraryThing, but they never got as popular as GoodReads, and I had friends who actually used GoodReads. So I moved on to GoodReads to be with my friends, since community trumps technology. I really wish I could have used the functionality of LibraryThing but still kept tabs with my pals. Sadly, these folks want to have a walled garden and don’t value interoperability.

Both of them ended up shutting down their APIs, which sucks because I wanted to use my data for me! I ended up routing around their damage.

But what a bunch of palaver!

I don’t want to give my data directly to Amazon (the owners of GoodReads). I don’t want to lose APIs or access to all the data that I’ve been putting in. I also care about my friends, but not that they use the same website as me!

So I was incredibly excited to discover a great book tracker in the Fediverse!

I think you should come with me and you should join BookWyrm.

BookWyrm is a social network for tracking your reading, talking about books, writing reviews, and discovering what to read next. Federation allows BookWyrm users to join small, trusted communities that can connect with one another, and with other ActivityPub services like Mastodon and Pleroma.

https://joinbookwyrm.com

BookWyrm is open source, decentralized and federated. It’s built on top of the ActivityPub protocol like Mastodon.

What does that all mean and why is it important?

BookWyrm is open source. The lead developer has a day job as a baker and isn’t trying to build an empire. When I wanted a feature that didn’t exist, I didn’t have to ask a product manager, I was able to open a Merge Request to contribute a solution! The documentation is also open and easy for anyone in the community to help improve.

BookWyrm is decentralized. That means it isn’t just one website like Twitter, GoodReads, FaceBook, LibraryThing, etc. It is made up of many sites – there are 22 sites live as I write this. If you don’t like one of them, you can leave and move to another, you’re not locked in to the choices and beliefs of whoever owns a server.

And Federated means that all these sites speak about books to each other in a special set of ways called ActivityPub. Some of these sites are for folks who speak a certain language or live somewhere or are interested in a certain kind of book… But if you have a friend on a different site, you can still be friends! The sites all speak to each other in a federation of small common websites. Bookwyrm has good people on it – you can find a good like minded community or span across communities.

And because BookWyrm speaks ActivityPub, it means that people who left Twitter for Mastodon can be friends with you on BookWyrm – they can comment on your books safely from their own community! It’s as natural as sending emails from your work to someone else’s.

And when I want to just get the books that I marked to-read so I can search for them across multiple places, I don’t have to spend a ton of time faking my way to get my own data. BookWyrm is here for me, not as a place trying to find a business model to exploit me.

So when you join BookWyrm, please – say hi and let’s chat about books– I’m @mttktz@bookwyrm.social!

Week 2207

Joe Biden is the Democratic nominee vs Donald Trump. No matter who wins, it will not be a progressive 4 years.

Met our neighbors Tim and Nicole – I think we met before, but it’s been a while. Talked about his very cool wakesurfing boat. He is way more conservative than me but very nice.

At work I’ve been working on a side project to improve documentation and it’s starting to really come together. I’m going to pitch it as a thing that should get funded with a team and see what happens. It’s hard to see a fun little project turn into something that is going to be implemented by more folks, but the tradeoffs on my time mean that once I’ve blazed out a rough trail it makes sense for other folks to lay down the rails and ties and get it productionalized. Projects where I’ve done that have had a good life after I’ve created them, so it always pays for me to hand them off and work on other big initiatives.

We’re also re-engineering tons of things. I’ve been looking at how we managed incidents and issues because relationships often get pegged to how a team performs in a crisis. In addition to incorporating some of my work like How to do a crisis call in technology, other people in the team turned me onto Crew Resource Management, which is how the folks in a cockpit handle similar situations.

Max finished his first ever chapter book! 90 pages of reading, a chapter or two at a time. He was really proud of himself. Just today as I’m writing this he’s one chapter away from finishing his next chapter book. His behavior has generally been really good too!

Zee has memorized Mr Nogginbody Gets a Hammer and she now wants to read to us for bedtime! She’s also asked me to teach her to read, so I guess it’s time to get into the ABCs again. We’ve put up letters on the fridge for her and some magnetic poetry for him.

Her counting is getting pretty good and we’ve been doing m&m math every time she goes potty. One worry with Zebus is that she’s started being really bitey – lots of little bites and nibbles and licks to indicate when she’s frustrated. Also, a little bit of hitting. It’s ok now with us, but if she was in a daycare I’m sure we’d be getting a call about it.

Week 2206

Politics

I donated money to the Alex Morse campaign, a progressive candidate who’s trying to unseat Richard Neal, a greedhead Democrat. That happened earlier, but recently it appears that there was a sex scandal accusation against Morse. He’s accused of having consensual sex with adult students at the university he teaches at that are not in his class and also messaging people he’s met on Tinder. Sexual harassment and consent are incredibly important, but weaponized accusations are exactly the sort of thing that conservatives have professed concern over. In Alex’s case, the investigation by the Intercept certainly makes it seem like people who want to work for Richard Neal have been manufacturing a scandal instead of uncovering one.

Other campaigns I’m looking closely at:

  • The State Slate – The great slate didn’t do great in 2018, but I still like these ideas and I’m willing to give again. These candidates are all good chances to flip a district and any campaigning they do is good for upballot races.
  • Donna Imam – an engineer who might be able to flip a texas district.
  • Dani Brzozowski

Family

The fam out at the Esopus Creek trail

We’ve been doing more hikes again. I’m trying to make sure the little monsters leave the house every day. We’ve been going out to the village a little bit as well. I haul the kids in our expandable wagon and we can eat at an outside restaurant called The Partition.

We’re getting an eensy bit more social (in safe and measured ways).

ZZ had an extended encounter with a nice lady named Alexa and her dog Chacho. They spent an hour hanging out and I can’t recall having a nicer meal in ages. Here’s pro tip – if you hang out with the children and amuse them while we have drinks and dinner I’m grabbing your bill!

Beer Club had a mini executive retreat when Ray showed up in Rhinebeck! We took the Ho’s to the FallingWater trail where I finally got to meet Finley! He loves Max and Zelda loves him.

The Scott’s dropped by! We took them out to Fallingwater as well, where Max and Ben got along really well and explored up the waterfall all the way to its source. Zelda is in love with Zoe and asks about her.

Max and Ben never usually play together, but for some reason this day was just perfect. Everyone got along famously.

DIY

Around the house, we’ve been struggling a little to knock out more projects. It just seemed like we lost steam. So we dug out the back yard next to our house and put in a bunch of marble rock chips over garden cloth. Now things are better looking and won’t require any weeding – instead of a dirt patch next to the house we have clean white stone which doesn’t need maintenance.

We cleaned out the trampoline, which had been under a mulberry tree, trying to become a mulberry jam strainer. Yecchh.

We spent a couple of meeting looking at adding solar panels to our roof – I really like it for a lot of reasons including my predilection for distributed systems over centralized ones. Sadly, the tradeoffs right now don’t seem worth it. Even with incredible financing and all sorts of incentives it would take forever to pay off the panels and require trimming trees.

This helped me feel like we can really start getting going again. I’m gonna finish that Patio!

Code and nerdery

Great news here! I’ve been thinking at work about ways to better handle and test documentation across multiple languages. The key here is to make sure that you can extract code samples from documentation and then push it out to a testable format.

I found mkcodes, an excellent tool for pulling code out of markdown documents. It worked great, but only for Python. I submitted a pull request extending it to work with multiple language code blocks and it was a real treat to work with Ryne Everett on getting this live. Which is to say, now it can handle java, dotnet, any other language you like that’s embedded in your docs. Expect more on this as I make progress building a docsite with eleventy.

I also type this on the linux laptop as I managed to resize partitions without destroying anything. I thought 80 gigs would be enough for my Ubuntu partition, but it seems to be growing and I had to give it a few hundred more gigabytes to grow.

Hosting better screencasts in WordPress

When I was writing up a note about pingplotter I figured I should show how it works because that’s much better for understanding it.

This gobbled up an incredible amount of time, so I’m writing it down so I never have to figure it out again.

There’s a few terminal screencast recorders out there, but I picked asciinema since it seemed simple, it recorder everything as text, not an image and it seemed a very light format to playback. These things are true, and the pain I felt is mostly unrelated to any failings of asciinema.

Asciinema is great. I can record a session locally, play it back to check locally and then upload it to their hosting to share. All pretty cool!

# record a session, but skip pauses over 1 second
asciinema rec -i 1 pingplotter.cast
asciinema: recording asciicast to pingplotter.cast
asciinema: press <ctrl-d> or type "exit" when you're done
# type a bunch of things to demo
# then ctrl-d to stop recording
asciinema: recording finished
asciinema: asciicast saved to pingplotter.cast

# time to check to see if it was any good
asciinema play pingplotter.cast
# the saved session plays back

# good enough!
asciinema upload pingplotter.cast 
# asciinema gives you the URL of the upload
# you can give it a description and make it public

Since this website is still powered by WordPress, I wanted to share it here. The Asciinema url didn’t embed automatically, so I figured I could either embed a script from Asciinema or add a plugin to do the embed code.

Both of these failed – somehow the javascript from Asciinema wasn’t working in my Firefox. It worked in Chrome, but that’s not good enough. I figured I should just host the cast myself! Why do I need to embed this tiny file or load javascript or install plugins to do this?

I found a neat utility to convert an asciicinema screencast to an animated SVG.

Listen, SVG and animated SVG is incredibly powerful, and if you don’t know, you should learn about it. Animated gifs are heavy, don’t resize, and are full of rendering artifacts. SVG are vector graphics, which mean they are infinitely scalable up or down crisply in a much smaller size than most gifs. Every modern mobile and desktop browser supports them.

The cool utility is svg-term. It lets you take an asciinema file and turn it into an animated svg.

It’s very nicely made, but is unfortunately distributed with npm, a package manager designed to ruin you. The instructions svg-term gives you to install it are incredibly dangerous unless you know to set up your npm install to not install packages with root privileges, WHICH FOR SOME REASON ISN’T THE DEFAULT OR EVEN POSSIBLE. I looked at the npm documentation for something like pip install --user, but it doesn’t exist. Instead, the solution is to redefine global installs to mean user local installs. I’m not kidding.

I don’t want to ever have to figure this out again either, so I added it to my jumpstart script so it will be ready for the next computer I use.

cat pingplotter.cast| svg-term --out ~/pingplotter.svg

This worked great! I went to upload it to my WordPress site to share with you. It did not work great!

WordPress very reasonably recognizes that while I enthusastically love SVG, it can be a security hole. SVG files can embed all sorts of foolishness like JavaScript and other loopholes to escalate privileges. So WordPress prevents them from being uploaded. I opted to allow them with a plugin that sanitizes SVG files to remove the dangerous stuff but allow the rich graphical goodness. This worked great!

I then decided to demo using tmux and glances, which meant I had to do a lot more typing in the demo, which expanded the size of the screencast. Poor svg-term began to crap out because I was exceeding the size of the javascript heap by feeding it a file larger than 1 mb. Fair enough, I don’t want to have a big file anyway!

The solution here is to script as much as possible ahead of time. The asciinema commandline takes a -c command argument which makes it execute a command as soon as it startes recording. Since tmux also lets you script up what you want it to do, I ended up with this.

asciinema rec -i 1 -c 'tmux new "pingplotter ; read" \; split-window "glances ; read" \; select-layout even-horizontal' pingplottertmux.cast

I’m telling asciinema to open up tmux and start recording. I’m telling tmux to start with pingplotter in a window, then open a new pane with glances and make the layout side-by-side. Each of those panes will close when the command they are running is done. Now I can just use ctrl-a <space> a few times to rotate the layout, ctrl-c to kill the panes, tmux exits and asciinema stops recording.

The file size ended up being small enough for svg-term and now you get this!

Anyhoo, black people are human beings with real lives and I don’t want my tax dollars to be used to murder them. Please, call your city representatives to urge them to shrink the police budget and use that money for social services that prevent crime and pain. If you can’t figure out your local representation, please learn how. If you won’t, please at least call your federal reps to ask them to stop giving the city police things like machine guns and tanks.