Skylar MacDonald
skye.fyi.web.brid.gy
Skylar MacDonald
@skye.fyi.web.brid.gy
Reluctant technologist. CAD & Technical Lead/nerd at London Ambulance Service. Tech stuff, NHS/999 stuff, occasional Excel witchcraft. Views, regrettably, my own. This is my blog!
Buying out my music library: how’s that going?
There has been something of an escalation in the MacDonald household. As you may remember, this time last year I told you that I’m trying to buy all of the music I regularly listen to because I think that’s _probably_ the right thing to do, for a number of reasons. I had, of course, failed to consider that what I had done here is invent a game. A game of which I was the only player, and could win by _also_ harnessing my innate desire to catch ‘em all. Essentially, I’d set up the perfect circumstances to ruin my own life.1 Anyway, to cut a long story short, in one single month I bought something like 35 CDs. Here’s what I’ve learned: ## I’m still buying out music I actually listen to The good news is, the smart playlist approach is working. I haven’t changed anything about my music listening habits, so it’s still clocking up the play count every time I listen to something in Apple Music. I’m then diligently referring back to this to decide what I need to buy next. So I think we’ve pretty definitively proven that this approach works very well. ## iTunes is good, but CDs are better As discussed previously, I’ve mostly been achieving this goal through purchasing individual tracks from the iTunes Store for 99p a go. This gives me a perfectly serviceable 256 kbps AAC audio file that is mine to keep — they don’t DRM purchases any more, and I was able to strip the DRM from some _very_ old iTunes purchases by simply deleting my local files and redownloading them from my iTunes account. Never again will I be disappointed by an `m4p` file! The thing is, though: that’s a super cheap way to get individual tracks, but not so great when it comes to albums. iTunes albums are all priced as ‘new’. Which is fair enough, I suppose — it’s not a used music retailer.2 But if I want to buy a whole album, these days it is not at all difficult to find that album on (used) CD on a website like musicMagpie for as low as a couple of quid. It’s exactly the same album, only this time it’s on a thin plastic wafer instead. This massively changes the maths. If it’s £1.99(!) to buy the used album, suddenly it makes more sense to do _that_ than buy more than two tracks from it. It’s all digital, so it’s just as good, quality-wise, as a brand new CD (or download). And this, readers, is how I ended up buying 35 CDs in November. Plus, there’s the quality argument. I’m not an audiophile; I mostly listen to music through a pair of AirPods Pro, which are pretty good but hardly the most impressive equipment on the planet. I have _access_ to lossless audio as a result of my Apple Music subscription, but I’d be hard pressed to tell you the difference between that and ‘standard’ quality. But I like that I now have the _option_ to import all the CDs I’ve bought at silly quality,3 even though I am almost definitely going to rip them in iTunes Plus format (i.e. 256 kbps AAC). ## Some strange stuff happens, particularly on Atmos songs Previously, I’d downloaded a lot of my library from Apple Music for offline listening (obviously caked in its own DRM). Unfortunately, a bug in the Music app on macOS means that when you then go to buy such a song from iTunes, it won’t let you, because it thinks you already own it. After all, it can see the file on your computer! So you have to go through and click “Remove Download” on these songs before it will let you buy them. Some songs were also downloading as `movpkg` files instead of `m4a`, which initially I found very strange. That, it would seem, is how songs in Dolby Atmos get downloaded onto your local machine. This is peculiar because I didn’t think the iTunes purchased versions of songs would be in Atmos — but I guess Apple Music is matching my purchases to its own library, so I still get to benefit from Atmos on songs I’ve bought (where available). You can turn this off in the Music app’s settings — untick “Download Dolby Atmos” and you should get the `m4a` files you’re expecting. (I _think_ the `movpkg` files have DRM; even if not, they’re not in a format that anything else understands.) ## It helps when you have iTunes credit to burn I will also admit that this process was massively helped along by a friend giving me an iTunes gift card that a family member was unable to use. That, I think, is what made it feel the most like a game to me. ## I bought an iPod This wasn’t part of the plan, I’ll concede. But because loads of my music is locally stored now, I figured, why not? It would be nice to be able to listen to my music in circumstances where it’s not possible or appropriate for me to have my phone on me. It turns out that syncing iPods very much still works as you would expect in modern macOS, except you manage it through Finder now instead of iTunes/Music. I gather there are also apps you can download on Windows to do it. I bought a 3rd generation iPod Nano, because that’s the best one they made. _I think you’re legally required to have this music video on this style of iPod._ ## I’m definitely going to keep doing this I’ve started now, so I definitely feel the urge to complete it, and I’m slowly changing the criteria of my “To buy” playlist to show me more and more songs. I suspect I’m probably not far off having bought the lot, or amassed it over the years on my various CDs and previous purchases. I think I will still keep Apple Music the streaming service, as I suspected in my previous post that I would — although I’m also planning on reviewing that decision every so often. If I _do_ decide to get rid of Apple Music one day, I think I will subscribe to iTunes Match instead. You may remember that this was something of a precursor to Apple Music; it would ‘match’ any songs you imported from elsewhere (like CDs or other stores) to the iTunes catalogue, and basically either give you the iTunes version for free or upload it to the cloud for you (to allow you to sync your library between devices). They do still sell it, and at time of writing it’s £21.99 a _year_ (versus Apple Music’s standard price of £10.99 a _month_), so would be a saving of over £100 a year if I chose to do that. I hope they keep selling it. There are limits to it that I would have to check I didn’t fall foul of, but I think the limit on songs that aren’t in the iTunes catalogue is 100,000, so I’m not too worried. ## In summary, then… This has been an interesting experiment and I’ve learned a lot about both my listening habits, and how susceptible I am to gamification.4 I’m looking forward to seeing how much further through this I’ve progressed this time next year. I’m particularly interested in whether I’ve spent all of that gift card yet, or whether I’ve actually got rid of Apple Music by then. I am also wondering if I will have some sort of new way of managing all these music files, like storing them on a NAS or self-hosting some kind of player. Both interest me, but I’ve not had the opportunity to do so before. But first, I’ll continue buying the music — let’s not run before we can walk! 1. This is exaggerated for comic effect. Everything is actually fine. ↩ 2. How on Earth you would run a used digital music retailer is left as a thought experiment for the reader. ↩ 3. “Silly quality” is how I’m describing FLAC/ALAC from now on. ↩ 4. I must absolutely never start playing any mobile game with microtransactions. ↩
skye.fyi
December 7, 2025 at 10:02 PM
Getting Rails tasks in Nova to work with chruby
**TL;DR: Set up chruby in your`.zshenv` file instead of `.zshrc`.** I recently bought Nova by Panic. I absolutely love it. I was formerly a die-hard Sublime Text fan — and I still am, really, for more ‘general purpose’ editing outside of a Project™ — but before that, I used to use Coda (RIP) by Panic, and Nova feels like its spiritual successor. Like most IDEs, Nova has tasks to allow you to run build scripts and that sort of thing. Because of its extensions framework, you can get loads of tasks that work with different frameworks and languages. (I promise this is not a sponsored post.) ## Ruby, Rails, and controlling snakes with gems Perhaps unfortunately for me given recent developments, I am a Rails programmer.1 But on the bright side, there’s an excellent Nova extension for Rails that makes life a lot easier. Until, that is, you go to run the dev server, and it does this: /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/bundler/lockfile_parser.rb:108:in `warn_for_outdated_bundler_version': You must use Bundler 2 or greater with this lockfile. (Bundler::LockfileError) Bother. It’s using the built-in macOS Ruby there, isn’t it? See, the world of Ruby has moved on, but macOS’s built-in utilities have not. Ruby is up to version 3.4.7 at time of writing, and as you can see above the version bundled with macOS is ossified at version 2.6.0. And, well, the big number at the front means breaking changes. I manage my Rubies on my Mac with chruby. That is one of many ways to do this, and you can argue amongst yourselves2 what the best one is. chruby requires you to `source` a script in your `.profile` or `.bashrc` / `.zshrc` file, which sets up the `chruby()` function used to switch different versions of it into your PATH. But the problem is, Nova runs its tasks in a **non-interactive** shell. And before I bought Nova, I had no idea what one of those was. Now, I use zsh as my shell on my Mac, which you probably do too if you have updated past Catalina or care about this sort of thing. You probably have to do this sort of nonsense for Bash as well, and as dearly as I love Bash I have only done this in zsh. Just so you’re aware. ## The thing I did not know Very, _very_ simply: * **Interactive** shells are the ones you can type in — one of which is your **login** shell. You can run commands in these shells and do all your usual stuff. * **Non-interactive** shells run scripts and you can’t touch them. Everything you need to access needs to be in your shell config when it starts, or your script will fail. The key difference: non-interactive shells **don’t source your`.zshrc` or `.zprofile` configs** when they start. This is really tricky to get to the bottom of, because when you open the _terminal_ in Nova, it **is** interactive (because you can type in it). Fortunately, those clever people who made zsh have thought of this, and you can create a `.zshenv` file in your home directory that _does_ get sourced in non-interactive shells. So, contrary to the instructions you’re given when you install chruby, what you actually want to do is set up chruby in there. So I stuck these lines in my newly-created `.zshenv` file: source /opt/homebrew/share/chruby/chruby.sh source /opt/homebrew/share/chruby/auto.sh And it _still_ didn’t work! ## Another gotcha I use chruby’s `auto.sh` script to automatically select the correct version of Ruby based on a `.ruby-version` file in the project root — but Nova _still_ wanted to run the Rails server with the built-in Ruby. How bizarre! `auto.sh` works by adding a pre-exec hook to run itself, like this:3 if [[ -n "$ZSH_VERSION" ]]; then if [[ ! "$preexec_functions" == *chruby_auto* ]]; then preexec_functions+=("chruby_auto") fi elif [[ -n "$BASH_VERSION" ]]; then trap '[[ "$BASH_COMMAND" != "$PROMPT_COMMAND" ]] && chruby_auto' DEBUG fi (The `chruby_auto()` function is defined further up the file.) It transpires that pre-exec hook doesn’t run in non-interactive shells. So you need to force `chruby_auto()` to run. Mercifully, this is as easy as adding it to the end of your `.zshenv`: source /opt/homebrew/share/chruby/chruby.sh source /opt/homebrew/share/chruby/auto.sh chruby_auto And as if by magic: It works! ## Et voila I mostly wrote this up so I can remember how to do it again if I ever need to. But hopefully that helps you if you find yourself in this situation. I imagine this also works if you’re trying to use chruby-ed Rubies in other non-interactive environments, but this is the context in which I had to figure it out. Merry chruby-ing! Image: Panic 1. Although _he_ and I have vastly different recollections of London. Especially given that I actually live here! ↩ 2. You may not argue _with me_ about this, because I don’t care. ↩ 3. See the full source file here. ↩
skye.fyi
November 11, 2025 at 6:57 PM
UniFi Talk call parking without UniFi Touch phones
Further to my mission to overengineer my landline, I’ve been playing around with the features it comes with, including the ability to park calls in a ‘parking lot’1 to pick up on another phone. All of the official UniFi documentation will tell you that this functionality only works on UniFi’s official Touch-series phones, because they want you to buy those — and, to be fair, it is probably a lot more slick on those. However, after a lot of poking around in `/usr/share/unifi-talk/app/server.js`, I think I’ve figured out how you can use it on non-UniFi SIP devices (which are generally significantly cheaper). ## The easy way: setting up a parking extension There are two ways you can set your system up to be able to park calls. The easiest way is within UniFi Talk itself: set up a Smart Attendant that does nothing except park a call, give it an extension number, and then you can transfer calls from your network into the parking lot at the extension you’ve chosen. First of all you’ll need to set up a parking lot, which you can do on the Call Settings page on your console. Name it something descriptive you can refer back to later. Head to the Smart Attendant page and create a new one. Name it something — mine is just called “Park”, but you might want to name the parking lot — and then click Next. On the second page after you’ve named it, make sure you give it an extension number; this field is marked as optional, but you’ll need it to make this work. Then click your way through the wizard steps, not enabling anything it doesn’t force you to. This will spit out an attendant that plays a message, but doesn’t do a lot else. Now, it’s up to you whether you want to play a message when your calls get parked, but I didn’t. So, if you click on the first blob in the flow chart — I believe this is the “menu” block — you can set it not to play anything. In the **Audio Message** section, set **Greeting** to **None**. Then you’re going to want to hover over the middle of the right border on this blob, and click the plus button that appears. These are your actions on this Smart Attendant, and so you’ll want to choose **Park Call** , and then select the parking lot you created earlier. That’s it. You now have an extension that will park calls. You can transfer calls onto that extension from any other device on your phone system and it’ll work just like you had one of those expensive UniFi phones. ## The hard way: transferring it directly to the parking lot’s SIP extension You can do this if you have the ability to dial an alphanumeric SIP address from your SIP device. Some devices will let you do this, and some very much do not like it one bit. I have found it’s worked best with softphones. Parking lots are identified by UUIDs, so you’ll need to find the UUID for your lot. This is exceptionally irritating to do, but if you open your Call Settings page with your browser’s dev console open to the Network tab, you’ll spot a request to `https://unifi/proxy/talk/api/parking_lots`.2 Look at that request and you’ll find all your parking lots as JSON, so you can grab the UUID of the one you’re looking for. Let’s say ours is `00000000-0000-0000-0000-000000000000` so you can spot it in my examples. Once you have your UUID, you need to know that the SIP ‘extension’ for your parking lot is of the format `parking_lot_uuid_[YOUR_UUID_HERE]`. Some clients will force you to add a hostname to the end so that they treat it like a SIP address; you can use `@unifi` for this purpose (or whatever hostname you’ve given your console). Some clients,3 on the other hand, will _refuse_ to work if you specify a hostname. If you transfer your calls to this extension, it’ll park it on that lot. You might want to add it to a softkey if you’re using a physical phone. (You will probably end up doing it the easy way as well, to be honest.) ## Picking up parked calls This is the _very_ annoying bit. UniFi really don’t want to make this easy on you, but of course call parking is no good unless you can pick up the calls afterwards. (Unless you just use it as a respite sanctuary for telemarketers.) The SIP ‘extension’ you would need to dial to pick up a parked call is of the following format: `unpark_[PARKING_LOT_UUID]_[CALLER_PHONE_NUMBER]` So ours might look like this: `unpark_00000000-0000-0000-0000-000000000000_02079460123` The same thing I said above about hostnames also applies here — you might have to add a hostname to make your device work, or you might have to very much not do that3 to make it work. Of course, this is then a complete nightmare to dial on SIP phones. I’m yet to solve this particular problem. But, if you know what your caller’s number is going to be — for example, I use this to park calls from my block of flats’ intercom in case I don’t get to the phone in time — you can hardcode the caller number into your device. If you figure out a better way to do this, do send me a WebMention! If you are on a softphone, of course, this might be possible to actually dial with the magic of copy and paste. Figuring that out is left as an exercise for the reader. ## The summing up bit This is all still a bit of a pain in the arse, to be honest. I hope to figure out a better way to pick calls off the parking lot, because otherwise I don’t really know how I can use this practically with my SIP devices. I particularly want to figure out a way to do it on my SIP ATA that won’t be able to dial alphanumeric characters, but I’m not sure whether that will even be possible. There is also the very real risk that a future software update will break all of this, because it’s only officially supported on the UniFi Touch-series phones — although hopefully that’ll change in the future? However, I can at least pick up the intercom if I’m not quick enough answering it normally. Image: Ubiquiti 1. I can only apologise for the American English. ↩ 2. You could also `fetch()` this URL in your browser console, if (unlike me) you can remember how to access the response JSON without having to Google it every time. ↩ 3. Looking at you, Grandstream GXP2170. ↩ ↩2
skye.fyi
May 3, 2025 at 4:51 PM
Using UniFi Talk with Andrews & Arnold VoIP
**TL;DR: Set it to PCMA instead of PCMU.** In my neverending mission to overengineer everything in my house, I decided to start running my landline using UniFi Talk. I like landlines (because of EISEC) and I broadly think everyone should have one, even in this day and age. But they don’t let you have proper copper ones any more, so we’re doing it over SIP now. And, hey — if you’re doing a little bit of SIP, why not do loads of SIP so you can make internal calls to your long-suffering partner? To get UniFi Talk to work, you need to either pay Ubiquiti £7.99 a month for a number through their proprietary system (which is actually just Twilio Elastic SIP Trunking under the hood — sorry for the spoiler), or you can attach your own third-party SIP trunk. I chose to do that, because despite how much I love landlines, I don’t really call people. (I mean, it _is_ 2025.) I chose Andrews & Arnold, because they’re a friendly UK-based ISP that offers a VoIP service, and they’ll happily let you use it for SIP trunking purposes. They also understand how to make EISEC work on a VoIP landline (which a lot of other services — especially those not based in the UK — don’t bother to do properly), and let you ring out to the proper UK phone network — so you can also ring NHS 111 if you manage to injure yourself whilst running PoE cabling through your ceiling. Just as a random example. I’m going to assume you already have an active UniFi Talk installation, because setting it up is relatively straightforward, but there is one caveat – you need a UniFi phone to activate UniFi Talk. No exceptions. Even if you are going to exclusively use significantly cheaper third-party SIP devices, you must have one UniFi Talk phone to activate the service. However, you don’t have to keep it after you’ve activated it, so you can borrow one from a friend or eBay it after you’re up and running. You don’t have to buy one of their numbers to make this work. I’m also not going to talk you through buying VoIP service from A&A, because their website is very easy to use. ## Setup on the Andrews & Arnold end On the A&A control pages, you should generate a SIP password for your phone number. It should be the same for both the incoming and outgoing setup. In the **Target** section of the **Incoming** tab, you will need to set your number up as **“to your server via SIP”**. Set your phone number in E.164 format as the username, use the _same_ password as you set at the top of the page, and enter your UniFi console’s hostname or IP address. I set up a hostname because I don’t have a totally static IP address. For presentation digits (**“Pres-Digits”** below), I picked **13** , so that A&A present CLI to UniFi Talk in E.164 format, which it seems to like. Set the delay to 0 seconds, unless you want your number to do other stuff first. On the **Outgoing** tab, make sure the SIP password is the same as on the other tab, and then put your WAN IP address in the **IP Access List** box. It’s recommended that you do this so other hosts can’t make VoIP calls using your account and rack up massive bills. You can also use this page to limit the size of the bills that can be racked up. That is everything you need to do on A&A to make it work. ## Setup in UniFi Talk Create a new third-party SIP trunk in the Talk system settings page on your UniFi console. Name the provider something useful like “AAISP” — it got weird about me putting the ampersand in, but your mileage may vary. Add the following custom fields, and set them as follows: Field name | Value ---|--- proxy | `voiceless.aa.net.uk` realm | `voiceless.aa.net.uk` registrar | `voiceless.aa.net.uk` from-domain | `voiceless.aa.net.uk` register | `true` username | _your A &A phone number in E.164 format_ password | _your A &A SIP password_ auth-username | _your A &A phone number in E.164 format_ When you’re all done it should look like this: My settings are in a different order because I was very much doing this by trial and error. It’s possible not all of these settings are required. I don’t know for certain, but they won’t do any harm. In the **Phone Numbers** box, enter your A&A phone number in — audience, say it with me — E.164 format. Routing-wise, it’s up to you — I’ve set mine up to handle all outgoing calls by default, but you might want to use A&A just for calls to the UK. The world is your lobster. And finally, add A&A’s IP address ranges to the **IP Address Range** section. The most up-to-date ones are on their support wiki but at time of writing they are: * `81.187.30.110/31` * `81.187.30.112/29` * `90.155.3.0/24` * `90.155.103.0/24` * `2001:8b0:0:30::5060:0/112` * `2001:8b0:5060::/48` Cross your fingers and click **Apply Changes**. ## It doesn’t work!! Go back to the Talk system settings page. Change the **Audio Codec** from PCMU (which is the codec the American and Japanese phone networks use) to PCMA (which is used by the rest of the world, and crucially, the only one accepted by A&A). This is **really** frustrating to debug in UniFi Talk, because it doesn’t tell you that this is the problem unless you trawl through your support file. Lo and behold, I saw a `415 Unsupported Media Type` error in mine, and that’s what motivated me to blog about this so that you too can fix this problem without having to turn on debug logging. ## That should be it… … but of course, you’ll now need to add your number to the groups or users you want to use it for. It will magically appear in the menus since you’ve put it in your trunk settings. You _don’t_ need to modify the UniFi Talk JavaScript files as you might have done in previous versions of UniFi Talk — the `from-domain` setting fixes the same problem that you’d previously have to have done that for. Enjoy your new landline. (And please don’t ring the emergency services to check your EISEC address is correct. Trust A&A that they’ve set it up correctly.) Image: Ubiquiti
skye.fyi
May 3, 2025 at 4:51 PM