Slightly too much Kodi for fun#
Wed, 04 Aug 2021 22:02:24 +0000
I'd been vacillating for a while about buying a new monitor, but eventually I pulled the hammer (is that the idiom?) on a spangly new Dell S2721SQ, which arrived yesterday and provided the incentive to look at NixElec again. Because it (the monitor) has speakers, which means I have the hardware to fix the audio issues without having to commandeer the family TV.
Second rate
I don't claim to understand how ALSA works, and Kodi's approach to ALSA is even more weird, but I did eventually make it work for 44.1kHz sources: define an ALSA fixed-rate pcm for Kodi that is hardcoded to S16_LE
format, and then tell Kodi about it in advancedsettings.xml
A sticky GUI mess
To the extent that Kodi can be configured through files, they're XML files. There is a toXML
builtin in Nix, but it only generates a particular XML representation that would need XSLT to turn into files that Kodi likes - and XSLT for me is assigned firmly to into the same "tried it once, not going back to that" bucket as are m4 and Java applet programming.
What I really wanted is something that would let me write out (or generate!) a nested attrset describing the structure I want, and turn it, possibly via JSON, into XML. Python's dict2xml is very nearly it, but has no support for XML attributes, so I had to invent something slightly more complicated.
Sadly, the extent that Kodi can be configured through files is not the full extent. Although the sources are defined in XML, the content of each source (tv shows? movies? music?) seems to be set in a Sqlite database, which is another level of complexity to manage. So there's still manual twattery on the GUI to deal with.
Hard pass#
Sat, 14 Aug 2021 21:31:26 +0000
I've got the key
I've got the secret
– From the Urban Cookie Collective's guide to password management
For reasons that seemed good at the time, I've written a password manager. It's a lot like pass ("the standard unix password manager") - which I have been using up 'til now - but it uses age instead of GPG to do the heavy lifting.
moss, the Maybe-Ok Secrets Store, is a 400-line Ruby script that uses only libraries provided by a default Ruby installation, plus 520 lines of testing code (Cucumber and RSpec).
Some random observations follow:
- almost all of it was test-driven, but the tests are for the most part end-to-end Cucumber tests that run the script in a subshell instead of calling classes/functions/methods directly. I was not expecting to get that far with end-to-end tests, but when all's said and done it's a pretty small system.
- There are certain deficiencies in the Cucumber step definition language that get more annoying as the test suite grows. Most notably, the use of ad hoc
@var
to pass state from one step to the next. I don't have a solution here, I'm just complaining about the problem. - I don't claim to be a security expert, which might make me not a good person to attempt this kind of project. I have carefully avoided rolling my own crypto by using a third party program for that, and I am quite pleased with my use of the various affordances of
Kernel.system
, IO.popen
, Kernel.spawn
and Tempfile
to avoid even passing plaintext or passphrases in or out of the Ruby process. There's a lot of flexibility in that family of methods if you read the docs. More information about my security-related design choices in the relevant section of the README - I am simultaneously proud and ashamed of the command line parser I created by using various metaprogramming features in ways that may or may not be generally recommended. (Don't try this at work, kids - at least, not if you work for my employer, metaprogramming in company code is firmly in the "presumption of bad" bucket).
- I didn't know, but now I do, that there is support in the Ruby standard library ('Fiddle') for calling C libraries without having to write extensions and needing a C compiler. I didn't actually need to - I thought I was going to need mlock(2) before I realised it wouldn't help and found a way to use a temporary file instead - but it's nice to know it's there.
- It's a single script with no dependencies other than a Ruby installation, and this is intentional: it means you can easily install it anywhere just by copying the script. I might reconsider the "single script" constraint if/when it passes 500 lines. I am less likely to renege on "no external dependencies", because I've too often had trouble running programs that require Bundler from inside a project with its own Gemfile.
It's been a long time since I wrote more than about 5 lines of Ruby for anything outside of a work context: for 'fun' projects I tend to pick languages which I don't get a chance to use 9-5. Ruby for this task was definitely less than awful, though.