A Weird Imagination

Saving shell transcripts

Posted in

The problem#

When writing a blog post like last time's, I often will be at least partway through the process before I realize it's interesting enough to write a post on. Then I need to somehow go back and reconstruct a narrative of the troubleshooting steps I performed.

The solution#

As long as I still have the terminals or screen sessions open, I can at least capture a snapshot of the scrollback history that's still available.

In Xfce Terminal (and likely similar in other terminals), I can save the history by going into the "Edit" menu and selecting "Select All" and then "Copy" (plain text) or "Copy as HTML" (to also capture styles) and pasting into a file or writing the clipboard to a file using xclip:

xclip -o -selection clipboard > some_file.html

In screen, the hardcopy command can be used to dump the full history to a file. Type Ctrl+a, : to get into command mode and run the command hardcopy -h to include the entire scrollback buffer. Unfortunately, this is plain text only.

If you use tmux instead of screen, the equivalent is the capture-pane command which does support saving styles if you provide the -e option. Note this encodes the styles so cating the file to a terminal will look right, not in HTML as Xfce Terminal does; you could convert it to HTML using aha.

The details#

Read more…

Unifi network controller failing to start

The problem#

I use Ubiquiti-branded network products for my switches and wireless access points and use the UniFi Network controller software to configure them.1 I noticed the web interface wasn't working and checked the status the service and saw the logs showed it starting the service and no further information (and looking at the log files didn't show it generating any errors either):

$ sudo systemctl status unifi.service
[...]
Aug 16 14:32:13 host systemd[1]: Starting unifi.service - unifi...

Specifically, this was on version 9.0.114-28033-1 of the unifi Debian package provided by Ubiquiti.

The solution#

After multiple steps of investigation, I finallly got the Unifi service running by changing the ownership of all of its data files to the unifi user/group:

sudo chown -R unifi:unifi /usr/lib/unifi/{data,logs}

But this might not have been the best fix: the first thing it did when I opened the web interface was tell me there's no database and offered to restore from a backup. Luckily I don't change the settings much and it did regular automatic backups (in addition to a slightly older manual backup I had), so I didn't lose anything. Also, it's possible the data loss was due to some other troubleshooting step, not that one. As always, keep backups and before making a change consider making an extra copy of the directory before the change (or making a snapshot if you're using ZFS or another filesystem that supports them).

The details#

Read more…

Checking for truncated videos

Posted in

The problem#

I've somehow multiple times ended up with corrupted video files such that they're cut off at some point, apparently due to a copy being interrupted or similar. As a result, I'm a bit paranoid about my video files not being what I expect, so I wanted a way to quickly check the length and view the start and end of many videos.

The solution#

The following script check_video_length.sh, takes any number of video files as arguments, and one-by-one, prints out the length in minutes, and player the first and last 10 seconds of the video (or less if you press q to quit MPlayer sooner):

#!/usr/bin/sh

for video in "$@"
do
    seconds=$(mediainfo "$video" --Output=JSON
              | jq '.media.track[0].Duration' | tr -d '"')
    minutes="$(echo "scale=2; $seconds" / 60 | bc)"

    echo "$video is $minutes minutes long."
    echo "Playing $video from start first 10 seconds..."
    mplayer -osdlevel 3 -endpos 10 "$video" >/dev/null 2>&1 
    echo "Playing $video from 10 seconds before end..."
    mplayer -osdlevel 3 -ss "$(echo "$seconds" - 10\
        | bc)" "$video" >/dev/null 2>&1
    echo
done

The details#

Read more…

Debouncing shell commands

The problem#

For a compile-on-save workflow where some computation is done in response to every change to a file, if there may sometimes be many changes close together, it may be wasteful to respond to all of them. This is often handled by debouncing the events: instead of responding to every change, ignore changes that occur too close together in time.

The solution#

watch_todo_debounced.sh is a modification of the watch_todo.sh script from my recent post on converting todo.txt files to HTML. It uses a script I found called debounce.sh to wait until there have been no updates to the todo.txt file for 5 seconds before generating the HTML file. The core logic looks like this:

while true
do
    inotifywait -e close_write "$1" >/dev/null
    echo "TODO file updated."
done | debounce.sh read $delay "./do-something.sh \"$1\""

$delay is the delay in seconds to wait before taking an action. $1 is file to watch for changes on and ./do-something.sh is the script to run on it when it changes.

The details#

Read more…

Show/hide part of page with only CSS, no JavaScript

Posted in

The problem#

A common use of JavaScript is to change which content is displayed on a web page. In some simple cases this can actually be done without JavaScript. While there's some older articles on how to do this, some newer HTML/CSS features can help.

The solution#

The simplest case is the <details> tag which allows showing a foldable section without even requiring any CSS. But for more complicated cases, variations on the "Checkbox Hack" can allow CSS to express more complicated rules about what to show/hide.

The examples in that article all rely on the content being revealed being a sibling of the <input> element defining the checkbox. That's less restrictive than it sounds because the <label> element that's the actual visible part of the checkbox can be placed anywhere. But even that restriction can be loosened using :has() as shown in this example simplified from the HTML/CSS used by my previous post:

<div class="filters">
  <label>
    <input type="checkbox" id="show_completed">
    Show Completed Tasks
  </label>
</div>
<div class="todo_list">
  <ul>
    <li class="completed">some task</li>
  </ul>
</div>
.completed {
  display: none;
}
.filters:has(#show_completed:checked) ~ .todo_list .completed {
  display: list-item;
}

The details#

Read more…

Simple remote view of todo.txt

The problem#

A few months ago, I wrote about using todo.txt to keep track of household tasks. The key word there being "household": many of the tasks I'm tracking are not performed on a computer. I mentioned this issue in the post, that I wanted to look into some way to view those tasks not from my computer.

The solution#

I wrote todotxt-to-html, a very simple script that takes a todo.txt file (with thresholds and due dates as supported by Sleek) and outputs it as HTML. Since Sleek auto-saves on every change to the file, using a script like my compile on save script, it regenerates the HTML file to keep it always up-to-date:

watch_todo.sh /path/to/todo.txt /path/to/webdir/

Also copy or link todo.css into /path/to/webdir/. Use nginx or some other web server to make /path/to/webdir/ available on your local network and then share the link to your smartphone or whatever other device you want to view your TODO list on.

The details#

Read more…

Devlog: kitchen timer: design

The problem#

Kitchen timers are common tool used by many (most?) people while cooking. They come in many forms, but tend to have the same basic user interface: turn a dial (physical or digital) or type in a time and press a start button, and get a notification (usually some kind of beeping sound) that amount of time later. Very simple concept, your kitchen probably has multiple on different appliances, some of which you might even use.

I've run into a couple ways this design does not quite fit what I want while cooking:

  1. For shorter measurements where being half a minute off matters, the time I take fumbling with the timer settings starts to feel non-trivial. In practice, I'll often just set the timer for one minute less than I actually want. Alternatively, you could make sure the timer is already programmed, so you just have to hit the start button. But both of these are workarounds for an interface not doing what I want.

  2. The timer is often just an estimate, and the reaction to the timer is to check on the food and set a new short timer for the next time to check on the food. This may happen multiple times, to the point where I've forgotten exactly how much additional time I've added and there's addiitonal gaps in time between hearing the timer and setting a new timer, so I don't know the actual total of how long something cooked unless I had memorized the time on the clock when it started. Some timers do start counting up after finishing which does help with this somewhat.

The solution#

I have not yet implemented a timer app, just thought about what I think would solve these problems.

The main interface idea I had was that the interface should not restrict the ordering of specifying the settings of a timer and starting a timer. The main entrypoint would be a big "mark time" button that would record a time point that could then be set as part of a new or existing timer as a starting or ending point, or just when an existing timer was extended for some additional time. Additionally, timers and time points should be able to be named, so you could have, for example, a timer named "turkey" and a time point labeled "temperature reduced".

I'm less clear on exactly what this should look like and how the history should be presented. The most important information is the amount of time left on the current timer and its name. And possibly the time since the very start and the time since the last named time point? This will require some experimentation, but since it will likely be used on a display with limited space (either a phone or a tablet that would optimally be visible from across the kitchen), the main display should have as little information as possible so it can be made as legible as possible.

The details#

Read more…

Devlog: Schedule Grid Editor

The problem#

Schedule grids (example) are a way of displaying a collection of events, some of which occur at overlapping times. They are often used to show what is happening at a conference or other busy event with multiple things going on simultaneously. They are tables where the y-axis is time and the x-axis is sometimes arbitrary or sometimes some concept of location (e.g., which room the event occurs in). Events are rectangles spanning their start through end times and usually covering a single column. This is a fairly standard display format for a calendar application showing a single day's events.

I had a friend who needed schedule grids for their job as a teacher in a (somewhat unusual) classroom that had a lot of small group activities, keeping track of where every student and staff person was supposed to be at all times. They were creating the grids using Google Sheets and spending a lot of time on the layout manually rearranging the columns and manually creating copies of the information to display both a summary and separate schedules for each person.

One complication was that due to privacy laws around information about students, I didn't want any of that data to be touching my server, both because I shouldn't have that data and I don't want to be responsible for promising the school district my server won't get hacked.

The solution#

Schedule Grid Editor (source) is a browser-based tool for creating printable schedule grids. While hosted on my server, the tool works just as well entirely offline. It saves the data in local JSON files (or locally in the browser using OPFS with support for import/export of JSON files).

It maintains a weekly schedule, where each event may be recur on one or more days of the week. Each event has some subset of the students and staff assigned to it, and the logic checks that no one is expected to be at two events simultaneously and that every student is assigned to some event at all times. For each day of the week, it generates a schedule with as few columns as possible showing all of the events. Additionally, for each day of the week, for each person, it generates a single column schedule showing just their events.

Read more…

Devlog: Resistance: Avalon web app

The problem#

I had been playing a lot of the social-deduction game The Resistance (and the version with more roles The Resistance: Avalon) and running into the problem that many players had trouble remembering exactly what had happened in previous rounds. Between the fact that there can be several votes throughout a game of the The Resistance and the game can sometimes take up to an hour, it can be hard to remember how the votes went a couple rounds back, especially for teams that were rejected.

The solution#

I created a web app implementation of The Resistance: Avalon (source) using Django.

Read more…

Devlog: Sprit Island helper

The problem#

The board game Spirit Island has all of players playing more or less simultaneously, especially when acting on different parts of the map, but requires some bookkeeping to be kept among all of the players. For a normal game of at most four players, this isn't difficult, but the game has rules to allow combining multiple copies to a huge game. My friend group planned a 12-player game and we wanted to figure out how to best keep the game organized. (Unfortunately, we planned this game for April 2020 and it did not happen for obvious reasons.)

The solution#

I developed a web app called fear-tracker (source), so called because the main shared information to keep track of is how much fear each player generates in order to keep track of the correct total generated by all players. It supports entering the fear generated per-spirit and the data is synchronized among any number of devices, so there does not have to be an exact correspondence between players and client devices. It also keeps track of what the current phase is as all of the players have to agree on some synchronization points for when new information is revealed by drawing cards.

The details#

Read more…