Posts tagged with “automation”

Using a Mac Mini as a bedroom PC for Kodi, Moonlight and YouTube

Issues in the bedroom

I have a wall-mounted TV in my bedroom. This TV has a PS4 and a PC hooked up to it. I use the PS4 to remote play my PS5 (which is situated in the living room), and in the past, have used the PC for Kodi/Jellyfin and YouTube in bed, using a USB remote control with support for moving the mouse cursor. Unfortunately, it doesn't do that very well, as the Wi-Fi adapter I have for it keeps presenting issues under Linux. No matter how much I re-compile and reenable the drivers, it just won't work properly, and will randomly cut out after a while. This also happens with several different adapters, so I've kinda just left it there, and used the PS4 to access the web UI of Jellyfin. This is not ideal, and I don't want to spend any more hours fighting with it.

Free fruit

I don't usually use Apple products, but I had a Mac Mini lying around as surplus after having replaced all Macs at my place of work with Windows computers. It's a shame to let it just lie around, collecting dust, so why not use it for something useful, and also solve my bedroom problems (heh) in one fell swoop?

My criteria were as follows:

Absolutely crucial

  • Support for my USB remote (it identifies itself as a keyboard and mouse so almost anything would fill this criteria)
  • Support for a wireless game controller (DS4)
  • Be able to run Kodi and Jellyfin for Kodi
  • Be able to run Moonlight, for remote playing PC games
  • Be able to watch YouTube with uBlock Origin and SponsorBlock
  • Be able to control the whole machine using just my remote and my gamepad

Nice to have

  • Be able to emulate some games natively, and use the gamepad to do so

Collection and assimilation

After stumbling upon Retro Game Corps' video on Retro Gaming on a Mac Mini, I figured that the Mac Mini I had lying around would be perfect for this, as long as the usual "Whoops! Can't do that on a Mac!" problems didn't stand in the way. I would use EmulationStation Desktop Edition (ES-DE) as the shell to launch Kodi, Moonlight and YouTube from.

I already had the Mac and the USB remote, but not an extra controller. As luck would have it, my step-brother's girlfriend had one she didn't need, so I inherited her bright blue DualShock 4, which is perfect. The other DS4 in the bedroom is black, and will continue to be hooked up to the PS4, while the blue one will be used with the Mac. Unlike in my experience on Windows, pairing and using the DS4 Just Werks™, and was ready to use right away after pairing.

Software setup

After installing the apps I wanted (Dolphin, RPCS3, RetroArch, Kodi, Moonlight), and ES-DE, I was ready to get going. I also installed NoMachine for remote access, in case I want to do maintenance on this machine without having to lie in bed to do so. It took a bit of web searching to find documentation for how to launch arbitrary apps from EmulationStation, but it wasn't really difficult.

By default, when you start ES-DE for the first time, it will ask you to create game folders for different systems, then exit so you can populate those folders with actual games. Do this, then additionally, create a folder called "Apps" in the folder you chose (typically ~/ROMs).

You can now make a custom system in ES-DE, by creating the following file:

~/.emulationstation/custom_systems/es_systems.xml:

<systemList>
    <system>
        <fullname>Apps</fullname>
        <name>Apps</name>
        <path>~/ROMs/Apps</path>
        <extension>.sh .SH .py .PY</extension>
        <command>open -a "%ROM%"</command>
        <platform>apps</platform>
        <theme>esconfig</theme>
    </system>
</systemList>

As you can see, this is pretty straight-forward. This will make a custom system named "Apps" in ES-DE, which gets its games from ~/ROMs/Apps, and will look for files with .sh or .py file extensions. Save the file, and you can now make the simple scripts that go into this folder.

For each app you want to launch from within ES-DE, create a .sh file in ~/ROMs/Apps and make them executable (chmod u+x script.sh. Here are mine:

Kodi.sh:

#!/usr/bin/env sh
open -a "Kodi"

Moonlight.sh:

#!/usr/bin/env sh
open -a "Moonlight"

YouTube.sh:

#!/usr/bin/env sh
open "https://www.youtube.com/feed/subscriptions?"

As you can see, these are simple as. The YouTube script opens your default browser to your subscriptions page on YouTube, but you can just as easily configure it to launch in a specified one, by using open -a "Firefox" "https://www.youtube.com/" if you want.

Save all files, restart ES-DE, and you should have a new category named Apps, containing your scripts. Launching the menu item will launch and give input focus to the app in question. Then use your gamepad/USB remote/whatever to navigate the given app. ES-DE and Moonlight works with gamepad navigation, but I've found that Kodi on macOS is hit or miss when it comes to this. It's worked a few times, but then stopped working, so I just use my remote for that.

Closing thoughts

A USB remote is usually a simple and cheap device, and can be found on eBay or AliExpress simply by searching for "usb pc remote control". Here is the one I use, which works great and costs next to nothing. This one has a button to switch between mouse mode and remote mode, which is important as you can't always navigate around macOS using the remote functionality alone.

There's lots of fun to be had in bed.... ;) Making Apple products an integral part of my bedroom experience isn't something I thought I would do, but it works surprisingly well. Now I have an all-in-one solution for gaming, movies, TV shows and YouTube content, all in one box, complete with uBlock to block YouTube ads, and SponsorBlock to skip sponsor segments!

For an extra smooth experience, you can configure macOS to open ES-DE when you log in by right clicking its dock icon and choosing Options > Open at login, and also making sure to uncheck "Restore windows [...]" whenever you log out/shut down the computer. I've also set both Kodi and the Mac to go to sleep after an hour of inactivity, so if I fall asleep, it won't run and use power needlessly throughout the night.


ntfy: Send notifications through libnotify to Linux desktop

I've recently started using ntfy to send notifications to my phone from some scripts I'm running on my home NAS. This works great, but when I'm on my PC, I'd rather get notifications there instead of on the phone. There doesn't seem to be a desktop app for ntfy, but luckily the API is extremely simple. I've also recently started picking up Python, so I decided to whip together a simple ntfy notification delivery system for the Linux desktop. To use this, you need notify-send (provided by the libnotify package), and python3.

ntfy-listener.py:

from sys import argv
import requests
import json
import subprocess

# Sends desktop notifications to a subscribed ntfy topic through libnotify/notify-send
# Usage: python3 ntfy-listener.py topic-name

if len(argv) > 1:
    try:
        resp = requests.get(f"https://ntfy.sh/{argv[1]}/json", stream=True)
        for line in resp.iter_lines():
            if line:
                ntfyData = json.loads(line)
                if ntfyData["event"] == "message":
                    ntfyTitle = "ntfy"
                    if "title" in ntfyData:
                        ntfyTitle = ntfyData["title"]
                    subprocess.run(["notify-send", "-u", "normal", ntfyTitle, ntfyData["message"]])
    except KeyboardInterrupt:
        exit()
    except Exception as e:
        print(e)

Launch the script with python3 ntfy-listener.py ntfy-topic-name , where ntfy-topic-name is the ntfy topic you'd like to subscribe to, and any incoming notifications will be delivered though your DE's notification system! I've additionally added it to KDE's autostart, so it loads in the background when I log in:

~/.config/autostart/ntfy-listener.desktop:

[Desktop Entry]
Exec=python3 /opt/scripts/python/ntfy-listener.py topic-name
Name=ntfy-listener
StartupNotify=true
Terminal=false
Type=Application

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.


Sending arbitrary files directly from Firefox to your phone

The task

Automation is great. There's just something inherently beautiful about the process of stringing together a bunch of software, services, or tools to attain a simple goal, and finding a solid solution that just works™. One automation task I've been tinkering with lately is how to send an arbitrary file directly from my browser to my phone, with as little fuss as possible. I often browse reddit or just the web in general and find a funny video or image I want to keep on my phone to send to someone, or just to easily refer to back later. If I can just click a button and nearly immediately have a copy of the resource in question available on my phone, that would be really swell.

Luckily, the world of open source software provides a multitude of ways to accomplish this task; here's how I did it.

The requirements

To follow along at home, you'll need:

  • A Linux-based computer
  • An Android-based smartphone
  • Firefox on your PC
  • The Open With addon for Firefox
  • yt-dlp (or youtube-dl or any of its forks) on your PC
  • KDE Connect on your PC (ships with the KDE Plasma desktop, or can be installed on most other DEs through your package manager)
  • KDE Connect on your phone
  • Optional: libnotify for notifications, pulseaudio for audio alerts

The solution

First, install the Open With addon into Firefox. Once that's done, follow the instructions it gives to set it up, it requires a helper script to be able to launch external resources from within Firefox. Install the KDE Connect app on your phone, and pair it with your computer. Now that that's set up, you can make a couple of scripts that the Firefox addon will run whenever you invoke it. The first one is specifically for video content, the second is for files.

send-to-phone-yt-dlp.sh:

#!/bin/bash
deviceName="Fold 3"
ytdlpPath="/opt/yt-dlp"
savePath="/home/lars/Downloads/%(title)s [%(id)s].%(ext)s"
errorSound="/usr/share/sounds/ubuntu/notifications/Slick.ogg"
successSound="/usr/share/sounds/ubuntu/notifications/Positive.ogg"

notify-send -u low "yt-dlp" "Starting download with yt-dlp..." --icon=camera-video
ytdlpOutput=$($ytdlpPath -o "$savePath" "$1" 2>&1)

if [[ "$?" -gt 0 ]] ; then
    ytdlpOutput=$(echo $ytdlpOutput | tail -n1)
    notify-send -u normal "Error" "${ytdlpOutput}" --icon=emblem-warning
    paplay $errorSound
else
    notify-send -u normal "Success" "Download successful! ($1)" --icon=emblem-success
    fileNameResult=$($ytdlpPath --get-filename -o "$savePath" "$1")
    kdeconnect-cli -n "$deviceName" --share "$fileNameResult"
    paplay $successSound
fi

send-to-phone-wget.sh:

#!/bin/bash
deviceName="Fold 3"
saveDir="/home/lars/Downloads"
errorSound="/usr/share/sounds/ubuntu/notifications/Slick.ogg"
successSound="/usr/share/sounds/ubuntu/notifications/Positive.ogg"

notify-send -u low "Download" "Starting download with wget..." --icon=unknown
cd $saveDir
dlFilename=$(wget "$1" 2>&1 | grep Saving | cut -d ' ' -f 3 | sed -e 's/[^A-Za-z0-9._-]//g')

if [[ "$?" -gt 0 ]] ; then
    notify-send -u normal "Error" "Download failed!" --icon=emblem-warning
    paplay "$errorSound"
else
    notify-send -u normal "Success" "Download successful! ($1)" --icon=emblem-success
    kdeconnect-cli -n "$deviceName" --share "$dlFilename"
    paplay "$successSound"
fi

You'll need to do some changes to these scripts depending on your environment:

  • Change the value of deviceName to the registered name of your phone in KDE Connect
  • Change the value of ytdlpPath to point to the yt-dlp binary on your system
  • Change the value of savePath to point to your preferred save location and filename of the videos downloaded by yt-dlp
  • Change the value of saveDir to point to your preferred save directory of the files downloaded by wget
  • Change the value of errorSound and successSound to the appropriate paths if you are not running a flavour of Ubuntu, or remove them altogether if you do not want audio feedback. In that case, remove all lines starting with paplay as well
  • Replace the lines starting with paplay with appropriate commands for your audio system if you do not use PulseAudio, but still want audio feedback
  • Remove the lines starting with notify-send if you do not want notifications or if you don't have libnotify installed

Don't forget to make the scripts executable! (chmod u+x /path/to/script.sh). Place them somewhere safe, i like /opt/scripts.

The next step is adding these scripts inside the Open With addon for Firefox. Click the Open With button in the toolbar, and click "Open With options". Click "Add browser". Fill in a name, and the path to the script with "%s" at the end, this is replaced with the URL when the script is invoked. Pick a custom icon if you'd like.

Repeat the same process for the other script, and you should end up with these two entries:

And that's really all there is to it. Now, whenever you are on a page that has a video you want to download and send to your phone, you can click the Open With toolbar icon, then "Send video to phone". If you're viewing a file, click the corresponding Open With item. This also works for links; If there's a link to an image or a file you want to download and send to your phone, just right click the link, go to "Open With", and click "Send file to phone (wget)", or pick the corresponding option if the link is to a video page.

Closing thoughts

Being able to send any video, picture or arbitrary file to my phone in two clicks is really convenient! The Open With addon is also really great for automating many other tasks that involve URLs, here are a couple of examples:

  • If a page doesn't behave/work in Firefox, I wanna open it in another browser. I have the Flatpak version of Ungoogled Chromium installed for that, but opening that, manually copying the URL from FF, tabbing over to Chromium, then pasting it in the address bar is a chore. Just add it to Open With: flatpak run com.github.Eloston.UngoogledChromium %s, and two clicks will open your current URL in the other browser (Note that this will NOT work if Firefox is running as a Flatpak, as flatpak run executed from within another flatpak will silently fail, in my experience, even with full permissions).
  • If I wanna send a link to JDownloader instead of opening it in Firefox, I can just add JDownloader to Open With, with the command /bin/sh /opt/jd2/JDownloader2 %s

I'm sure there are many other uses for this approach as well, get creative!