Posts tagged with “kde”

Aa!! It's a zorting zombie!

I came across an interesting Bash issue today, as I was trying to restore a zstd-compressed CloneZilla Partclone image to a raw file in order to extract some data from it. For some reason, none of the solutions on the internet worked, and searching for the error message turned up no useful results. This was the command line I had constructed:

$ zstdcat nvme0n1p3.ntfs-ptcl-img.zst.* | sudo partclone.ntfs -C -r -W -s - -O image.img

Notice the glob at the end of the only argument to zstdcat. This only gave me:

Partclone v0.3.17 http://partclone.org
Starting to restore image (-) to device (image.img)
This is not partclone image.
Partclone fail, please check /var/log/partclone.log !

partclone kept saying This is not partclone image no matter what I did. I did some sanity checking with the commands:

$ ls nvme0n1p3.ntfs-ptcl-img.zst.*

and also

$ ls nvme0n1p3.ntfs-ptcl-img.zst.a[a-x]

Both returned the following list:

nvme0n1p3.ntfs-ptcl-img.zst.ab  nvme0n1p3.ntfs-ptcl-img.zst.ah  nvme0n1p3.ntfs-ptcl-img.zst.an  nvme0n1p3.ntfs-ptcl-img.zst.at
nvme0n1p3.ntfs-ptcl-img.zst.ac  nvme0n1p3.ntfs-ptcl-img.zst.ai  nvme0n1p3.ntfs-ptcl-img.zst.ao  nvme0n1p3.ntfs-ptcl-img.zst.au
nvme0n1p3.ntfs-ptcl-img.zst.ad  nvme0n1p3.ntfs-ptcl-img.zst.aj  nvme0n1p3.ntfs-ptcl-img.zst.ap  nvme0n1p3.ntfs-ptcl-img.zst.av
nvme0n1p3.ntfs-ptcl-img.zst.ae  nvme0n1p3.ntfs-ptcl-img.zst.ak  nvme0n1p3.ntfs-ptcl-img.zst.aq  nvme0n1p3.ntfs-ptcl-img.zst.aw
nvme0n1p3.ntfs-ptcl-img.zst.af  nvme0n1p3.ntfs-ptcl-img.zst.al  nvme0n1p3.ntfs-ptcl-img.zst.ar  nvme0n1p3.ntfs-ptcl-img.zst.ax
nvme0n1p3.ntfs-ptcl-img.zst.ag  nvme0n1p3.ntfs-ptcl-img.zst.am  nvme0n1p3.ntfs-ptcl-img.zst.as  nvme0n1p3.ntfs-ptcl-img.zst.aa

Wait... What? Notice how ab is the first and aa is the last substitution. Obviously, aa needs to be the first substitution! They need to be zstdcated in order for partclone to recognize the file, as I assume there's a file signature/magic bytes at the start of the raw file contained within the archives.

My question was, why is my alphabetization seemingly broken? According to a web search, Bash globs will always return an ordered/alphabetized list. I was able to start the restore process by manually entering the list of files in order instead of globbing, but curiosity got the better of me and I had an inkling this was related to my system locale.

SPOILERS: It was.

$ locale
LANG=en_US.UTF-8
LANGUAGE=
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC=nb_NO.UTF-8
LC_TIME=en_GB.UTF-8
LC_COLLATE=nb_NO.UTF-8
LC_MONETARY=nb_NO.UTF-8
LC_MESSAGES="en_US.UTF-8"
LC_PAPER=nb_NO.UTF-8
LC_NAME=nb_NO.UTF-8
LC_ADDRESS=nb_NO.UTF-8
LC_TELEPHONE=nb_NO.UTF-8
LC_MEASUREMENT=nb_NO.UTF-8
LC_IDENTIFICATION=nb_NO.UTF-8
LC_ALL=

Observe how LC_COLLATE=nb_NO.UTF-8. I have my system language set to English, but most other locale settings set to Norwegian. In Norwegian, Aa/aa is a common substitution for Å/å, the last character of the Norwegian alphabet. The sorting algorithm, in its infinite wisdom, seems to have decided that a file extension of *.aashould be sorted at the very end because of this, which breaks the argument list.

To fix this, I set LC_COLLATE to C by issuing:

$ sudo localectl set-locale LC_COLLATE=C

This worked for about three seconds before KDE decided that my opinion is wrong, and promptly overwrote it, resurrecting nb_NO.UTF-8 like a digital zombie.

In KDE's System Settings > Region & Language section, there are a few locale related settings, but nothing about sorting. Presumably, it uses one of the other fields to assume the value of LC_COLLATE, and you know what they say about assuming.

To actually fix it, I added export LC_COLLATE="C" to my ~/.bashrc, which seems to work, and persists between terminal sessions.


"Retrocorder" - Using a spare PC to make a fully automated retro-gaming recording solution

Persistent nostalgia

The past few months, I've spent some time to set up a room in my house to be a pretty sweet space dedicated to retro-gaming. This includes a vintage PC, a CRT TV, and a multitude of classic games consoles hooked up to four RCA/AV switches. This works well, and to play the console we want, one can just flip a couple of switches instead of having to unplug and shuffle around literally 8 different sets of RCA cables.

Thing is, when you're playing games, especially during couch coop, things tend to happen. Some of these things, depending on the types of games you play, can be utterly hilarious, and worth saving for posterity. With the two last generations of consoles I own, PS5, PS4 and the Nintendo Switch, you can just press a single button on the controller to save recent gameplay as a file to the system storage, which is awesome. When you also enjoy playing on 20-30 year old game consoles as well, this part becomes a bit trickier.

Emergent technologies

Enter: The "Retrocorder" - the dedicated computer I've set up to continuously record all retro gaming gameplay as it happens.

For reference, this is what my setup looked like before Retrocorder:

(If I stick with 3-port switches, I can only get one more console before I have to add another level to the hierarchy 😳)

Since all the signals involved here are analog, you'd think there would be some degradation at each step. That might be true, but none are perceptible to me in regards to image or sound quality, and the cables between the switches are short, minimizing signal loss.

Hardware

If you want to follow along at home, here's a parts list:

The above links are affiliate links. If you buy something using them, I may earn a small commission at no extra cost to you.

Using these parts, we can split the RCA cables right before the TV, and lead one end to the TV, and the other into our capture card, like so:

As you can see from the chart, the splitters are inserted between the first switch and the TV. This way, no latency is introduced to the TV, so we can play our games normally without any changes to our gameplay. We can also swap game consoles mid-session and have no interruptions to the video recording, since the video is captured from the first switch.

Regarding the 3-6x RCA cables listed: 3 of them go from the first split of the RCA Y splitter to the TV. As for the other 3 - depending on how your splitters and AV2HDMI box look, you may or may not need them. I was able to plug the second split of the RCA Y splitter directly into the AV2HDMI box without using the extra 3 cables - your mileage may vary.

The reason I recommend the HDMI capture card over the RCA/AV capture card, is because AV capture cards tend to have issues switching between NTSC and PAL video signals. I mix NTSC and PAL video signals all the time, as I own consoles and games of both regions. My CRT TV supports both signals, which isn't a given for any CRT TV either, so make sure yours does before you mix signals and end up with unusable video files. The HDMI route isn't immune to this issue either, but the AV2HDMI converter box seems to handle this a lot better, and seems to send the same signal to the capture card regardless of input. I also experienced horrible buzzing noises during recording when using my AV capture card, presumably due to interference, but that might be down to a faulty card that's been stored in a box for 10 years or more.

The AV2HDMI converter box works great, and will detect a change in signal automatically. In my testing and usage, I have found only one combination of consoles/game regions that will throw it for a loop - playing NTSC games on a PAL GameCube (via Swiss). This outputs a black and white, wavy signal from the AV2HDMI box, presumably because the GameCube is outputting an esoteric (or out-of-sync) signal that it doesn't quite understand. The TV shows the signal just fine, however. Playing PAL games on the same console works fine, so I've solved this issue by just using PAL games instead. The PS2, where I also mix NTSC and PAL games on a PAL console, does not exhibit this problem, and both regions display fine when captured.

Software

Now that all the hardware is plugged together, it's time to make this as seamless as possible using software.

For the operating system, I use my default go-to for projects such as these: The latest Kubuntu LTS. KDE is my favourite desktop environment, I'm very familiar with it at this point, and the Ubuntu LTS base provides a solid foundation.

The hardware of the PC is modest, as this is an ancient PC that I used as a daily driver 10 or so years ago, only upgraded with an SSD:

  • CPU: AMD FX-6100 Six-core @ 3.300GHz
  • GPU: AMD ATI Radeon HD 7870 GHz Edition
  • RAM: 8 GB
  • SSD: 128 GB

After installation, we can start installing some software, and setting some desired options.

I've installed the following:

  • NoMachine - for remote access from my main PC
  • OBS Studio
  • obs-cli - Make sure you get you get the one from pschmitt - the other ones I've found are all defective or outdated

Now, set up your scene in OBS. At the bottom, add a new video source.

I had to pick YU12 to get the correct image for my setup, but yours may be different. The AV2HDMI converter box outputs a 720p or 1080p signal, configurable by a physical switch on the unit itself. I set mine to 720p, and the recording resolution to 576p, as no console in my setup outputs anything higher than that over AV anyway.

Now that that's out of the way, we just need to configure a few things about the recording environment. In OBS, go to File > Settings, then Output, and set Output Mode to Advanced. Click the Recording tab, then set your desired settings:

My changes from the default:

  • I've used /opt/retrocord as the folder to store all my recordings.
  • I've checked "Generate File Name without Space"
  • I've set the video encoder to x264 as it is light on CPU and produces files of a manageable size
  • I've enabled "Automatic File Splitting"
  • I've set "Split Time" to 5 min

The reason I've enabled automatic file splitting is twofold: To not end up with files of gargantuan sizes, and to let me delete older files in the directory when it becomes too large, without having to split file and review what I want to save.

Click OK. Next, choose Tools in the menu bar, then click WebSocket Server Settings. Check Enable WebSocket server, then click Show Connect Info. Note down the Server Port and the Server Password, as you'll be needing it soon. Close the connection info window and click OK in the WebSocket Server window.

Scripting scripts

Your recording environment should now be ready to use. The next step is to automate this, so you don't need to manually interact with OBS to make it do its thing. I accomplish this using two scripts; a login script and a logout script.

/opt/scripts/retrocorder-start.sh:

#!/usr/bin/env bash
obs --startrecording &

/opt/scripts/retrocorder-stop.sh:

#!/usr/bin/env bash
obs-cli --host localhost --port 4455 --password abcde12345 record stop
sleep 1
kill -s TERM "$(pidof obs)"
sync

In retrocorder-stop.sh, you need to change two values in the second line: the port and the password that you noted down earlier (4455 and abcde12345 in the example above). The reason we need obs-cli in the first place and the OBS WebSocket Server to be running, is that while OBS can let you send it an argument to start recording, it has no such argument to stop the recording in progress (for some god-awful reason).

As you might have noticed, this setup might end up with a full disk after enough usage, so we're gonna have to deal with that with another script. This time, we're gonna set up a cron job to be run once an hour, to prune the oldest videos in the recording directory, once a size threshold has been exceeded:

/opt/scripts/retrocorder-prune.sh:

#!/usr/bin/env bash
cd /opt/retrocord || exit
limitBytes=$((50*1024*1024*1024)) # 50 GiB

currentDirSize="$(du -bs | awk '{print $1}')"
if [[ "$currentDirSize" -gt "$limitBytes" ]]; then
    while [[ "$currentDirSize" -gt "$limitBytes" ]]; do
    purgeFile=$(ls -rt *.mkv | head -n 1)
    rm "$purgeFile"
    currentDirSize="$(du -bs | awk '{print $1}')"
    done
fi

This script deletes the oldest files in a directory until the total directory size is less than 50 GiB. Change the second line of the script to point to your recording directory, and the third line to reflect how much disk space you'd like to allocate to recordings. I've set mine to 50 GiB, as that is plenty, and leaves lots of headroom on the 128 GB SSD.

On my setup, 5 mins of recording equals around 100 MiB. This means that I can record > 42 hours of gameplay before the script starts purging - more than enough time to save any clips I want to keep!

Quick maffs 50 GiB * 1024 MiB per GiB = 512,000 MiB allocated
512,000 MiB / 100 MiB per 5 mins = 512 files á 100 MiB
512 files * 5 minutes per file = 2560 minutes
2560 minutes ≈ 42.66 hours ≈ 1.77 days
 

Finally, put the script in your crontab. First, edit your crontab:

$ crontab -e

Then append a new line at the end of the file:

0 * * * * /opt/scripts/retrocorder-prune.sh

Save and close the file (if the editor is nano, press, Ctrl+X, then Y, then Enter).

This will make the script run once an hour.

Setting settings

Note: These settings are for KDE Plasma. There are most likely equivalents for these settings in all other major DEs.

Once the above scripts are created, in Plasma, go to System Settings > Startup and Shutdown > Autostart. Click Add at the bottom of the window, then pick Add Login Script. Navigate to and pick /opt/script/retrocorder-start.sh. Now do the same for the logout script: Click Add > Add Logout Script, then navigate to and pick /opt/scripts/retrocorder-stop.sh. This will automatically start and stop recording when you log in and out of the computer.

To make this completely automatic, you'll also need to make sure you're automatically logged in to the computer. Also in Startup and Shutdown, pick Login Screen (SDDM), then click the button labeled Behaviour on the bottom left. Next, check the box next to Automatic log in, then choose your user and session on the same line. Click Apply.

Also in Startup and Shutdown, pick Desktop Session, then uncheck the box next to Logout Screen - Show. This makes sure when you request a shutdown, it is done immediately.

The next destination is still in System Settings, under Power Management this time (called Energy Saving in earlier versions of Plasma). Uncheck all checkboxes, then check Button events handling. In the drop-down box When power button pressed, pick Shut down.

Lastly, if you wish access this computer over SSH, install and enable openssh-server:

$ sudo apt update
$ sudo apt install openssh-server
$ sudo ufw allow ssh

This allows you to log in remotely via SSH. Additionally, it lets you use FISH to easily copy files from the Retrocorder to your main machine; SSH-enabled servers can be accessed in Dolphin by using the fish: URI scheme in the address bar:

fish://192.168.0.123/

You could also set up an NFS or SMB share, but that's out of scope for this post.

Headless chicken

Note: This section mostly applies to desktop PCs. If you're using a laptop, you're more or less done.

At this point you should be in a state where everything is automatic. Starting with the PC off, when you press the power button, the computer will boot, log you in, start OBS and start recording. Once an hour, your recording directory will be checked, and if it's too big, the oldest files will be deleted. OBS will keep recording until you hit the power button. Once you hit the power button, OBS will stop recording, close, the disks will sync, and the computer will turn off.

Wouldn't it be cool if you didn't need that pesky monitor, keyboard and mouse?

In most cases, if you don't have a display attached, the computer will not boot to a graphical environment. There's two ways to fix this, either by creating a dummy display (Xorg only), or by getting a physical dummy connector which will fool your computer into thinking a display is attached. There are dummy connectors available for all sorts of display connectors, but this post will focus on the software solution, as it works great for me.

This following solution only works on Xorg. I don't know if Wayland has an equivalent method of making a dummy display, but I'm sure you could find something by searching the web.

Create the following file:

/usr/share/X11/xorg.conf.d/xorg.conf:

Section "Device"
    Identifier  "Configured Video Device"
    Driver      "dummy"
EndSection

Section "Monitor"
    Identifier  "Configured Monitor"
    HorizSync 31.5-48.5
    VertRefresh 50-70
EndSection

Section "Screen"
    Identifier  "Default Screen"
    Monitor     "Configured Monitor"
    Device      "Configured Video Device"
    DefaultDepth 24
    SubSection "Display"
    Depth 24
    Modes "1280x720"
    EndSubSection
EndSection

If you're not on *ubuntu, xorg.conf might live somewhere else, such as /etx/X11/xorg.conf. In many cases, it doesn't exist and must be created - searching the web is your friend again here.

Save this file, which configures a dummy 1280x720 display. In my experience, increasing the resolution doesn't do anything, the dummy display seems to max out at that resolution.

Restart your computer, and recording should now start, even without a display attached! It will also enable you to remote control the desktop using NoMachine or equivalent remote control software.

You should now have a fully automated recording solution for your retro gaming setup! :) The only thing you need to do now is press the power button to turn the computer on and start recording, and press it again to shut down when you're done.

Here's a sample of gameplay recorded from my Retrocorder. It's by no means perfect, but it's more than good enough for my purposes - saving fun or memorable bits of gameplay. This clip is me playing the 2006 PS2 game Black:

YouTube truncates this to 480p, so if you want the source file, you can get it here!

Closing thoughts

The first time I tried booting my PC after setting up the dummy display, it would not start up, much to my annoyance. Turns out this motherboard is one of those who will halt the boot process if a keyboard isn't connected - easily fixed by changing the BIOS settings not to halt on keyboard errors.

This was a fun project to set up!


KDE Shenanigans: Playing a random video from Dolphin

Dolphin, the KDE file manager, is great, and has grown to become my favorite file manager of all time. It's super customizable, and a joy to use, which is more than I can say for the Windows equivalent. I do a fair amount of file management, so having a good tool for this is important, and when it's extensible like Dolphin, that's when it really starts to shine.

I recently got the idea to make a script that will play a random video file from a given directory tree. Some possible use cases for this is to play a random episode of a TV show, or a random home recording stored on your computer. Making the script itself was fairly straight-forward, but I don't want to open up the terminal to launch my script every time I want to use it, and I have enough keyboard shortcuts for things already (the most important one being Meta+Z, which plays a rimshot sound effect, much to the amusement of everyone I know).

Naturally, I started looking into integrating this into Dolphin. Initially, I wanted to make a custom toolbar button, but it turns out that isn't possible. What you can do however, is make a KDE Service Menu! These live in the context menu that pops up whenever you right-click things. They are really easy to create as well, you just pop a suitable .desktop file in the right directory, make it executable, and presto! You got yourself a context menu item! Let's see how to accomplish this.

Making the script

First of all, let's make the script itself. There are many ways to go about this, and I just went with the most straight-forward way I could think of; recursively reading the files of the current directory, filtering them on extension, and picking a random one out of the list.

playrandomvideo.sh:

#!/bin/bash
shopt -s nullglob nocasematch
matches=$(find . -print | grep -i -E "\.(webm|mkv|ogv|mov|avi|qt|ts|wmv|mp4|m4v|mpg|mp2|mpeg|mpe|mpv|flv)$" --color=none)
IFS=$'\n'
read -r -d '' -a matchedFiles <<< "$matches"
numFiles=${#matchedFiles[@]}

if [[ "$numFiles" -gt "0" ]] ; then
    rand=$((0 + $RANDOM % $numFiles))
    randFile=${matchedFiles[${rand}]}
    xdg-open "$randFile"
    exit 0
else
    kdialog --sorry "No videos found in the current directory tree."
    exit 1
fi

Note that if you use some esoteric video format that is not included in the regex pattern on line 3 of the script, you can just add it. You can also replace the list of file extensions entirely if you want to adapt the script to opening a different type of content; why not live life on the cutting edge and replace it with ppt|pptx|odp, so the next time you have a presentation at work, you won't know what you're presenting until you start it? Way to keep yourself on your toes.

Place it somewhere safe, like /opt/scripts, and make it executable with chmod +x playrandomvideo.sh.

Making the service menu

Prior to doing this, I didn't know how to create service menus, but KDE has great documentation on how to do that.

First, find the location of service menus on your system, and cd into it. Create playrandomvideo.desktop, and make it executable.

$ qtpaths --locate-dirs GenericDataLocation kio/servicemenus
/usr/share/kio/servicemenus
$ cd /usr/share/kio/servicemenus
$ sudo touch playrandomvideo.desktop
$ sudo chmod +x playrandomvideo.desktop

Note that if your path is in your home directory, you do not need to use sudo to touch and chmod the file.

Now open the file in your favourite text editor, and populate it with the following:

playrandomvideo.desktop:

[Desktop Entry]
Type=Service
MimeType=inode/directory;
Actions=playRandomVideoFromHere
X-KDE-Priority=TopLevel

[Desktop Action playRandomVideoFromHere]
Name=Play random video from here
Icon=media-playback-start
Exec=cd "%u"; /opt/scripts/playrandomvideo.sh

Change the contents of the last line to match where you placed the script we made earlier.

The line X-KDE-Priority=TopLevel is optional. If you keep it, the context menu entry will appear at the top level of the context menu, like so:

If you omit the line, the context menu item will live under a submenu named "Actions":

Done!

Now you can right click any folder, or any empty area of the current folder, and click "Play random video from here" to do just that. The video will open in your system default handler for its respective file type (using xdg-open). If no videos are found, you'll be notified via a dialog box.