A series of unfortunate kludges for doing content analysis of radio

Talk radio is arguably the single most understudied source of political and news content. The main reason we don’t give it the attention it deserves is simple – accessing and archiving it is a hassle. It’s the same reason Facebook has always gotten disproportionately less attention than Twitter, only talk radio’s role in American politics goes decades further back and connects much more directly to organized strategic communication than Facebook’s. I’ve thought about this problem for a while, and could never crack it. I even bought a secondhand Griffin Radio Shark but could never find the proprietary (and long out of support) software to get it to work the way I needed.
The good news is I did eventually figure it out, thanks to most radio stations now having their broadcasts freely available as digital streams, and I’m going to describe what I figured out in this post. The bad news is that what I figured out is one ridiculous kludge after another, and is absolutely not the optimal way to do this. No guarantees are offered and no support will be forthcoming. I hope this works for you! And if you know the way to do this that’s not three AppleScripts in a trenchcoat, please let me know. For clarity, I’m doing this on a 2023 Mac mini running Ventura 13.5. And for SEO purposes, let me specify that this post describes a method for recording multiple audio streams at the same time. Here we go!
First I’ll lay out the specific thing I needed to be able to do. A colleague and I wanted to archive a year’s worth of local news and news-related media content from South Florida – that’s two TV markets, five daily newspapers, and lots of radio stations, although only eight of them are in the news-talk format. One station doesn’t have any locally produced content, and one is an NPR affiliate that has its local stuff easily available as podcasts. But the other six could only be accessed live, and of course their shows overlap a lot, especially in the morning – between 6:00 and 9:00am, all six are running local programs. That means I needed a way to capture as many as six streams, starting and stopping each at arbitrary times throughout the day.
I started with just figuring out how to do scheduled recordings, which was easy. I’m using Audio Hijack, which allows you to create multiple sessions and have multiple scheduled recordings per session. Specifically for this project, it can capture the audio output of one specific application, and mute that application while recording, without affecting anything else on your system.

But now two problems arise. I need to start and stop the stream concurrent with Audio Hijack’s recording schedule (it can launch another app to record, but it can’t click a play button within it), and I need to be able to do this for as many as six independent browsers, because Audio Hijack can’t differentiate between tabs. On top of that, I needed this to work on different sites. Four of our stations are on iHeart.com, one is on radio.com, and one is just on its own site. I was able to work out an AppleScript to make an iHeart station play in Safari, but everything else was one strikeout after another. I could have probably figured out the underlying JavaScript a decade ago, but the code operating these sites today is above my paygrade, and Chrome, Firefox, Brave, et al., didn’t play nice with AppleScript.
Here’s where it gets kludgy. Look away if you cannot stomach bad practice that works well enough.
First, I needed browsers, and I wasn’t super keen on finding six different browsers to run, keeping them loaded up with these six pages, and remembering what was what. Instead I’m using Unite to create web apps for all but the self-hosted station, WFTL. Unite takes a URL and creates a standalone browser app with that page, which, crucially, the system and Audio Hijack understand as distinct programs. That means they can each be separate audio sources. For whatever reason, it choked on the WFTL page, so instead I just use Safari for that one. As an aside, I understand the version of Safari that ships with Sonoma can also create web apps, so that may work as well.
Now that I had separate browsers, I still needed something to get the streams starting and stopping. I gave it my best shot, but I couldn’t find any way to script interaction within the code of the page, so instead I brute forced it. For each time I needed to start or stop a stream, I wrote a script in Automator (saved as an app specifically – just compiling and running the script didn’t work) that brought the app to the front, paused five seconds as a buffer, and did a click at the coordinates of the play/stop button. For more than one stop or start at a time, I put another five-second buffer and repeated the script.

Using coordinates means two important things. First, you can’t move the browser window, or at least if you do you need to update the coordinates (you can get these by doing a partial screenshot with ⌘-shift-4, which shows the coordinates next to the cursor, and then escaping out). Two, your display cannot be off or asleep at the time the script runs, because the system needs the display active to make sense of the coordinates. Dimmed is fine; off is not.
Now we have unique applications streams and script to operate them. Once I figured out when I need to start and stop them all, which for six stations that have 11 total programs was a puzzle, I created a script for each different time that something needed to happen. Two stations start at 5:00am, so I have a start script for just those two, for example. In total I have start or stop scripts that run at X times throughout the day. I get them to run by using the Calendar app, which can trigger an app launch at the time of a calendar event. I also give starts a two-minute buffer to account for preroll ads and early program starts, and stops a one-minute buffer for late program stops. So the first event in the calendar is at 4:58am and the last one is at 6:01pm. If you want to do this every day, you can just set the events to repeat. We are randomly sampling days, so part of my process is editing the events at the end of a recording day to set the date to the next recording day.
This all gets the streams going; we also need to get Audio Hijack recording them. I created a session for each specific timeslot that is used, whether by just one station or multiple – e.g., one session for the two stations that have 4:00-6:00pm shows, which I set up as 3:58-6:02pm. If this all works, you will not hear anything. Audio Hijack will be recording (and thus muting the sources by default, though you can have speaker output if you want) the entire time the streams are running.

At the end of this process I have 11 MP3 files ranging from two to four hours long (file format, audio quality, save directory, and all sorts of other things can be set to whatever you want). Then I do two other things. First, I use Fission, from the same developer as Audio Hijack, to edit out preroll ads from the beginning of my recordings. Next, I use MacWhisper to do transcription. It does a variety of languages, including the two I need, and does a pretty good job. On my system it can do an hour in about 14 minutes. The format of the text it produces isn’t ideal (you still have to do some manual cleanup to split different speakers, for example), but it’s much less expensive than what I would consider comparable solutions.
A couple of important notes, in line with “don’t move the browser windows” above. First, create an account on any site that allows it. iHeart will nag you to do so, and the nag window may screw up the effect of the click to start or stop. Because this is streaming web audio, it’s still subject to network and other web weirdness. Some platforms will continuously retry if the connection drops, but WFTL, for example, does not; it just stops. That means if a network problem occurs, the stream just goes silent, and the scripted click to stop actually restarts it, no longer muted by Audio Hijack. Again, it’s kludge on top of kludge. I’d love to be able to find the actual source streams and just plug them into VLC, and maybe someday that will happen. Until then, this is good enough.