The Agony of 24/7 Discord Streaming & AutoHotkey

There is one piece of knowledge I was trying to get to, and in doing so I had to learn alot of individual things. The goal was, figure out how to keep a discord stream running 24/7 with how modern computers & my less than stable ISP connection are constantly interrupting all of my shit.


Let's rewind a bit, and get some background on why.

Markiplier feeding Ethan Nestor scrambled eggs with a spoon taped to a Fuck Machine. This is one of the last Unus Annus videos before they deleted the channel.

Long time friend of the show EntranceJew, at the start of the pandemic, had an idea to create a discord based TV station. All the tech folks were stuck working from home anyway, so why not watch Perfect Strangers and Head of the Class while we do it? To accomplish this, EntranceJew wrote TV Time version 1, written in Unity with emojis as the interface buttons. Essentially, what it did, was it communicated with VLC to queue things up and kept track of what had already been queued, so that three weeks from now, I can know exactly where I left off on Comedy Bang Bang & pick it up from there.


The rest of this is kind of lost in the blur of the pandemic, I remember sitting in my apartment in New Jersey and working through sourcing things that were widely regarded as good, culturally relevant, or hard to find. Things that were worthy of being seen during the difficult isolation of Covid.


There was also a youtube feed, you could pop links to anything youtube-dl compatible in the dedicated channel for it, and a bot would run it through youtube-dl and queue up the file. Lots of it was manual. You had to Parsec into EJ's laptop to queue things up & would rearrange the playlist to make it so that you didn't just have six hours of star trek but instead it rotated through a few shows throughout the day. We spent alot of mornings asking eachother if we had already queued up Good Mythical Morning, since the links would get deleted from the text channel. I created a new youtube account and wrote a python script that would hourly feed in all the new videos from that account's subscriptions. Sometimes discord would update and take the stream offline. Sometimes windows updates would forcibly reboot the laptop. Sometimes VLC would simply no longer be able to play videos unless you closed it and restarted. Zombie VLC processes would hang around and intercept the queue commands, even if it seemed like VLC was closed.


The media library endured two hard drive failures. The first one necessitated a rebuild of the library, there were spreadsheets listing what we had and lots of time was spent trying to re-source shows we had lost in the failure or finding new shows. Youtube helped fill the gap. EJ sent off the disk to a data recovery company, because there was some hard to find stuff on there like Downtown: Gaki No Tsukai. From what I remember, they recovered quite a bit.


Some time later, there was a second hard drive failure. This one took the program with it. We had now lost our way to ingest youtube videos & manage our library. Free Time was not on our side, so for a while there was only manual management by dragging files into the VLC playlist. EntranceJew eventually wrote TV Time Version 2, which was now... I want to say electron?


At some point earlier this year the laptop hosting things died. We were without even a computer to stream from... My new goal had been set: find a way to, with what I know now, keep a TV station going with minimal intervention.

I installed an old Windows 10 Ameliorated Edition iso I had laying around on the computer I built in 2013 in order to keep it from interrupting the stream.


For this, I'm using Jellyfin (media server) and ErsatzTV (program what can turn the media libraries into HD Home Run compatible tv stations). Setting up a channel in ErsatzTV and then feeding it into Jellyfin's live TV feature is pretty straightforward, all things considered. You need a browser to watch stuff in and I went with Ungoogled Chromium to keep things simple. ErsatzTV took a bit of time to wrap my head around, but ultimately it works. The stream is going through discord, no surprise there. I got a little HDMI dongle to trick windows into thinking it has a monitor screen, since Parsec didn't want to forward a display that didn't exist.

Jellyfin's Live TV channel listing

But I'm not done, what I have here is great, except for a few key problems that need solving. The first one is that discord updates more than any other software that's ever existed. Every third day I see the green update arrow. If you ignore it for too long Discord just does it for you. When it does that, stream's outta here, it won't restart on it's own. The second, is that ErsatzTV occasionally seems to have problems finding my video card. The third? Jellyfin, sometimes, will get "stuck" and the page needs to be refreshed. The fourth, and probably the most annoying one, is that my home internet connection is not particularly stable. When I first moved in it was dropping out three times a day. I had to call verizon to troubleshoot, they updated my ONT or something and now it goes out only once a day. I don't have it in me to set aside the time to call them again. It goes out just long enough to kill the stream. Babysitting the stream got real old real fast.


So! Now I know the problems. Let's talk solutions, in the order I tried to implement them.


AutoHotkey, as you may know, is a favorite among complete fucking nerds. And as a complete fucking nerd, who actually now has a use case for AHK other than "i wanted to have my CTRL+ALT+T linux shortcut work on windows," I've found it to be pretty good for this, with a couple of caveats. What if I set up an AutoHotkey script to restart the stream? That should be easy enough, I'm just clicking on the same spots on the screen over and over. And that part works great. But we have a problem- I can't kill and start the stream every 10 minutes, that's not a great experience.


So now I needed a way to detect if Jellyfin (via Chrome) is pulling enough bandwidth for me to be confident it's running, and if discord is sending enough bandwidth for me to be confident the stream is going out. This is pretty difficult without third party tools. The first way I did this was with powershell, but it seems like with powershell, a technology I fucking despise, you can't get process-wise usage, you can only get it off the network interface, which means there's other traffic mixed in. Certainly task manager must be running some kind of powershell command to come up with those per-process charts? Seems like something I should be able to do.


I'd also need to know if discord had an update pending. Autohotkey can check the color of the pixels where the arrow should be and if they're green then let's do the update and wait. What if an update ran without me knowing and knocked out Discord? I can use a function called IfWinExist() to check if the process already exists, and then that "selects" the process for me to maximize it. Same for Chromium - if chrome.exe isn't running I can start it up and maximize it.

A snippet of the AutoHotkey Script showcasing the runas.exe functionality

The script, with these things in mind, worked like so - check for updates. if so, update, if not, run two powershell commands and dump them to a text file. If the up or down speeds seem too low, restart the stream by clicking disconnect from voice, entering about:blank in the URL bar, clicking the bookmark for the tv channel in jellyfin, clicking play, switching to discord, and doing all the clicks that you have to do to re-join voice, start a stream, and pick the appropriate window. This worked well enough, but had alot of false restarts, and for some reason Parsec can't seem to interact with anything if AutoHotkey has taken focus. I've yet to find anyone else having this issue, I do not have a solution, I'm sorry if you're looking for one. My solution for that problem is walk upstairs and plug in a monitor.


In my quest to improve this process, I learned about a program called AppNetworkCounter, which can output a .json file that contains network usage for each process. It requires running as administrator, which means to spawn it from AHK without popping a UAC prompt, the AHK script has to run as administrator. I wrote a quick and dirty python script to parse out chrome.exe and discord.exe so that AHK can read those numbers.


Did you know? Parsec can't touch anything that's running as administrator, and since I had to elevate the script, this now means Chrome is an administrator process, and if you fullscreen chrome guess what, on parsec you can't un-fullscreen it.


This brings me to the weird part of this post - there doesn't seem to be much use for elevating to administrator just to spawn an unprivileged process, and yet, here I am. I tried a bunch of things in Powershell but came up empty, eventually I stumbled on runas.exe, a tool that's built into Windows. You can specify what user to run as, regardless of the context in which you launched the process. You can also tell it to save the credentials, so that it won't prompt for them. I've essentially elevated and de-elevated a process simultaneously. It's the silliest thing I've ever done with a computer and I do stupid shit all the goddamned time.


Once I had this sorted, I set out to solve the ErsatzTV cuda initialization error... turns out this usually just means the docker host had a driver update & the container needs to get restarted. I can't do it via AHK, since the containers for Jellyfin and ErsatzTV run on a different computer. but I have a cron job running now that greps for "cuvid initialization error" in the ErsatzTV logs, and if it finds it, it stops the container, renames the log file, and starts it back up again.

A screenshot of the ErsatzTV logs directory, with a few that have been renamed due to the cuvid initialization error.

But, with all these peices of the puzzle, I'm able to launch the AHK script as administrator, call AppNetworkCounter as administrator, run a python script to parse that JSON file and then delete it, and then conjure up Discord and Chrome as unprivileged processes.


There are still a couple of problems to solve. One is that I write my logs to a samba share so that I can look at them from wherever. If the computer that samba share is on is rebooting when the script does it's little dance, AHK pops an error and just sits there until I tell it to ignore the script. I'll have to look into exception handling later. Yet another is Discord. Discord keeps asking me to buy nitro. I do not want nitro. Discord has become feature bloat incarnate. I will not pay for things I don't want. These ads, reminding me I only have three days to get a special discount on a nitro subscription often move or block the UI elements, and AHK isn't necessarily smart enough to know that's what's happened, because it happens in seemingly a different way every time.


A new thing I'm wanting to do now, and I'll have to get this figured out and will maybe follow it up, is I'd like to have a second mode for binging a show. The Jellyfin interface is different for shows versus live tv, so the play button is in a different place, and just sorta behaves differently. My initial thought is to have a configuration file that AHK can parse to get coordinates depending on what mode I'm in, or change the URL for a show vs a tv channel. Maybe something in JSON, but I'm not sure how to parse JSON from AHK, it'll have to be a problem for future me.