Return to Blog Home

Polishing Twitch User-Markers

Source 

Polishing Twitch User-Markers

It's functional, but only for me, next was to make it usable for other users.

My goal for this session was to make it usable to others. First was not to lock all the features behind keyboard shortcuts, I added a <details> and <summary> to contain a Menu and Add button below the bottom-right corner of the video. Finding the correct element to append my <details> did take a moment, but eventually I found it and added it, and additionally made my waiting code also wait for this element to be into view before continuing the script.

Bug-Hunting

Next issue was the fact that when the user was focused in a dialog <input> or <textarea>, the Escape key didn't allow them to close the dialog - an issue I ran into a number of times. This I was able to blame past-me on, as I had the Escape key action ignored if the event.target had a tagName of 'INPUT' or 'TEXTAREA', so removing this check did the job. Or so I thought, in order to support Alternate Player for Twitch. I needed to stop all keyboard event propagation, to not trigger the keyboard shorts added by it. While I'm sure there exists a way to have best of both worlds, as I no longer used Alternate Player for Twitch.tv, I decided to drop support for it entirely.

In the beginning I was using Alternate Player for Twitch.tv's internal API to determine the current broadcast time when adding chapters live, hence why it became supported.


Next issue - another one I personally faced - was that if one added a chapter and instantly started typing the chapter name, the first few characters would not get inputted into the <input>. This was again thanks to past-me, for some paranoid reason I had my pre/afterCreated/post dialog JS methods executed 250ms after the dialog was created. Making this instant makes the input instantly focused with on perceivable side effects.

I also remove another artificial delay I had included when scrolling the chapter list to current chapter on open.


While my manually-reactive logic for the chapter list was quite a mess, it was reliable - in all but one features, chapter deletion. The chapter listing would simply still be in the UI after deletion, which was only because I never called remove() on the <li>.


The next issue was actually one I had introduced - the userscript completely broke on live streams. Perhaps "issue" is an understatement in this case, turns out my selector for the UI didn't exist when live, therefore the userscript waited infinitely for the element to appear in the DOM. The solution was to find a common selector for both the VOD and live views that I could add my UI to, which I did partly find: elements with classes starting with metadata-layout__.

Refactoring

The chapter list was shoved into the middle of the code in an IIFE, this would not do - it was nearly half of the main script lines. It took a bit extracting it out and passing all the needed functions to my generateChapterList(), but I got it abstracted, making the primary script.ts much more cleaner.

Keyboard Controls

When I had last been adjusting the latest #100Devs timestamps, I realized that if I could use keyboard shortcuts to adjust chapter details from within the chapter list I would be much more efficient:

While I hadn't all the actual keyboard behaviors planned out - I knew I wanted chapter navigation to be bound to vertical keys, and horizontal movement to decrease/increase the seconds, I only had rough ideas for the rest of the keys - 'n' to edit the name, '[' and ']' to seek forward/back.

I knew that having these keys customizable would be a nice feature, but for now, like the other keyboard shortcuts, I'd be hardcoding to what worked best for me.


Implementing these actions wasn't too difficult, but before that I had to fix a little bug that made the "active" chapter lag behind the actual active chapter. Turns out the issue was that while the current time when watching the VOD was down to a second, the current time when watching live was down to the millisecond, so my code to find the current chapter did not find the current time equal to the current chapter time thanks to the difference in milliseconds. Nothing a little Math.floor() couldn't fix though, and with that the active chapter was being correctly highlighted.

Now currently the "active" chapter was updated every second, by checking what the current VOD time was and then selecting the active chapter based on the seconds. This wasn't good enough for quick keyboard controls though - I couldn't wait for up to an entire second after switching between chapters to actually have the UI update. So I decided to introduce a data-attribute, so I could more easily mark which chapter is active and wouldn't have to wait for the interval to update it.


Finally the keyboard controls were implemented, and they had so far been helpful when editing timestamps, meaning that as far as features, the userscript had both everything I needed and wanted. At this point all that was left for me to do was to perhaps improve the UI, create some basic tests, and write up a quick README.md.

Publishing

Thankfully there was little to do to actually "publish" my userscript, so I made the README.md, created the repo, and pushed it! I lied, there was an extra step in publishing this; building the JS file and making it easily obtainable. As this userscript uses TS, I have to run npm run build to generate the userscript that can actually be used. Well what's the issue, just add it to the repo and call it done, right? This would clutter up the repo itself with distributed assets, which I didn't want to do. I also didn't want to have to do releases or anything that complicated, so I opted for making a dist branch that contained the latest transpiled JS ready for copying.

For good measure I whipped up a little .sh shell script to automate the build-and-push-to-git process for this dist branch.