A Weird Imagination

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…

Clipping videos from the shell

The problem#

Sometimes I have a long video that I only want a shorter section of. Maybe it's a TV show that I want to clip a funny scene out of. Or a video of a concert that I want to clip the individual songs out of so I can put them into my music library. But figuring out exactly where to start and end the clip so there's no weird sounds or flashes due to accidentally including the surrounding video takes some care.

The solution#

In order to determine the right clip location, I wanted an easy way to repeatedly watch and listen to the start or end of a proposed clip and get immediate feedback on adjusting its position frame-by-frame.

find_split is a script that implements this workflow. You give it a video, a proposed frame index to split the video on, a preview context length in number of frames, and whether the preview should be the video before (to the left of) or after (to the right of) the split. It plays the preview window ending or starting at the proposed split and waits for the user to press a key indicating how to move the proposed split and then repeats, starting by playing that preview. Once you've confirmed you've determined the desired frame index, press ` (or just Ctrl+c) to exit. The full keyboard controls are described at the link.

$ ./find_split video.mkv 1000 60 left
Playing video.mkv on left of split at 1000 \(with 60 extra frames\).
Playing video.mkv on left of split at 1060 \(with 60 extra frames\).

The second line appears after typing Shift+h to increase the proposed frame index by 60 after the first clip finishes. That means the second clip played will be from frame 1000 to frame 1060, so about two seconds (60 frames) long starting at approximately 33 seconds into the video. You can continue to adjust the position until you've decided you've determined the correct frame.

Once you have found the start and end frames (here 1060 to 2034), you can save the clip to a file:

$ ./encode_frames video.mkv 1060 2034 clip.mkv av

(Change the av to audio if you only want the audio and not the video.)

The details#

Read more…

Interpreting keypresses in shell

The problem#

Last time, I presented a script read_keypress.sh which would read any single keypress in a shell script. For printable characters, it's straightforward what this means, but for keys like left arrow (🠈), it's not obvious how to deal with them.

The solution#

keytest.sh demonstrates using read_keypress.sh to respond to keypresses including 🠈 and Ctrl+c:

#!/bin/bash

esc=$(printf '\e')
ctrlc=$(printf '\x03')

while true
do
  key="$(./read_keypress.sh)"
  case $key in
    q|"$ctrlc")
      echo "Quitting..."
      exit
      ;;
    "${esc}[D")
      echo "Moved left."
      ;;
    *)
      echo "Unexpected key: $key ($(echo -n "$key" | xxd))"
      ;;
  esac
done

The details#

Read more…

Handling keypresses in shell

The problem#

For certain applications it can be useful to get many quick responses from the computer, so we would like it to react to individual keypresses instead of requiring an entire command to be typed out and confirmed by pressing Enter. While this is a common feature of GUI and TUI toolkits (e.g., ncurses), it can also be useful for very lightweight inactive experiences written as simple shell scripts.

The solution#

read_keypress.sh will wait for the next keypress and return it as a string:

#!/bin/sh

STTY=$(stty --save)
stty raw -echo
SEL=$(dd if=/dev/tty bs=1k count=1 2>/dev/null)
stty "$STTY"
echo -n "$SEL"

The details#

Read more…

Thunar custom actions

The problem#

While I use the command-line and keyboard-based interfaces a lot to manage files, I also regularly use graphical and mouse-based interfaces. As with any time there's different ways of interacting with a system, there's some times when in one mode when you want the features of the other.

The solution#

Xfce's file manager Thunar has a feature called custom actions, which lets you add menu items that run commands on files or directories you are viewing or selecting.

The documentation suggests multiple ideas of actions you might want, including converting among different formants, rotating images, and various file management actions like viewing disk usage and bulk moving files. Additionally, the default action "Open Terminal Here" is one I use frequently.

The details#

Read more…

Playing Android motion photos

The problem#

My Android phone's camera app has an option to take a "motion photo", which, similar to the iOS "live photo" feature, records a short, silent video along with the photo. When viewing the photos on the phone, there's an option to play the video. But image viewer programs on my computer like Eye of GNOME (eog) do not support playing them.

The solution#

ExifTool (in Debian, the package name is libimage-exiftool-perl) can extract the video to a file using this example from a forum post:

exiftool -b -EmbeddedVideoFile photo.jpg > photo_motion.mp4

You can also play it directly without saving it to a file by piping to VLC:

exiftool -b -EmbeddedVideoFile photo.jpg | vlc -

The details#

Read more…

Viewing Signal messenger backups

Posted in

The problem#

Signal is a privacy-focused instant messaging application which ensures end-to-end encryption on all messages and generally goes out of its way to avoid accidentally revealing your conversations to third parties. As is common with security mechanisms, this necessarily adds some friction some some tasks you actually want to do. In particular, this means viewing your message history outside of the official app is unsupported. Which is especially a problem due to the app being tied to a smartphone that will likely only continue being usable for at most several years. So there's no official way to maintain access to your conversations indefinitely in contrast to IRC and Pidgin logs I have going back decades.

The solution#

The Signal Andriod app supports making encrypted backups (there is no way to get messages out of Signal iOS).

When you enable backups, it gives you a "passphrase" (a sequence of 30 digits). If you have lost this passphrase, there is no way to recover it, but you can disable backups and then re-enable them which will generate a fresh passphrase that will be used for your backups going forward. I recommend storing this passphrase in a password manager like KeePassXC.

To actually use the backup file, you'll need to know where it is. The location is shown on the Signal settings under Chats -> Chat backups -> Backup folder. You can copy it to your computer by plugging the phone in with a USB cable and using an MTP client, which is included in most file managers (I use XFCE's Thunar).

Once you have the backup file and passphrase, you can use signalbackup-tools to convert the backup to something human-readable:

signalbackup-tools [input] [passphrase] \
                   --exporthtml [directory] --allhtmlpages

The output does a good job of trying to look like the Signal interface. See the documentation for more options on controlling the output including various options to filter the output to just some conversations or time ranges.

The details#

Read more…

Move window to current desktop

The problem#

Previously, I wrote a script for opening and immediately focusing xfce4-appfinder. But xfce4-appfinder will notice if it's already open and just assume you want to use the existing window. Even if it's on a different desktop. And therefore attempting to focus it will either do nothing or switch to that desktop, neither of which is desirable.

The solution#

The actual solution I settled on is more of a workaround than a solution: having xfce4-appfinder on a different desktop doesn't really make sense, so I just set it to be on all desktops (specified as the non-existent desktop -1), so it would never be on the wrong desktop:

xdotool set_desktop_for_window "$winid" -1

To actually move the window to the current desktop, replace xdotool windowactivate "$winid" with

desktop="$(xdotool get_desktop)"
win_desktop="$(xdotool get_desktop_for_window "$winid")"
if [[ "$desktop" == "$win_desktop" ]]
then
  xdotool windowactivate "$winid"
else
  xdotool set_desktop_for_window "$winid" -1
  xdotool set_desktop_for_window "$winid" "$desktop" \
          windowactivate "$winid"
fi

Although this assumes that you know the $winid of the window you want to move. If you have just the title it works just as well to use

wmctrl -R "Application Finder"

The details#

Read more…