Bypassing Region Restrictions on radio.garden

I must be the last person on Earth to have heard about radio.garden (thanks Pepsilora!), a website that uses a “globe” interface to let you tune in to radio stations around the globe. But I’d only used it for a couple of minutes before I discovered that there are region restrictions in place. Here in the UK, and perhaps elsewhere, you can’t listen to stations in other countries without using a VPN or similar tool… which might introduce a different region’s restrictions!

How to bypass radio.garden region restrictions

So I threw together a quick workaround:

  1. Ensure you’ve got a userscript manager installed (I like Violentmonkey, but there are other choices).
  2. Install this userscript; it’s hacky – I threw it together in under half an hour – but it seems to work!
Screenshot showing radio.garden tuned into YouFM in Mons, Belgium. An additional player control interface appears below the original one.
My approach is super lazy and simply injects a second audio player – which ignores region restrictions – below the original.

How does this work and how did I develop it?

For those looking to get into userscripting, here’s a quick tutorial on what I did to develop this bypass.

First, I played around with radio.garden for a bit to get a feel for what it was doing. I guessed that it must be tuning into a streaming URL when you select a radio station, so I opened by browser’s debugger on the Network tab and looked at what happened when I clicked on a “working” radio station, and how that differed when I clicked on a “blocked” one:

Screenshot from Firefox's Network debugger, showing four requests to a "working" radio station (of which two are media feeds) and two to a "blocked" radio station.

When connecting to a station, a request is made for some JSON that contains station metadata. Then, for a working station, a request is made for an address like /api/ara/content/listen/[ID]/channel.mp3. For a blocked station, this request isn’t made.

I figured that the first thing I’d try would be to get the [ID] of a station that I’m not permitted to listen to and manually try the URL to see if it was actually blocked, or merely not-being-loaded. Looking at a working station, I first found the ID in the JSON response and I was about to extract it when I noticed that it also appeared in the request for the JSON: that’s pretty convenient!

 

Composite screenshot from Firefox's Network debugger showing a request for station metadata being serviced, followed by a request for the MP3 stream with the same ID.My hypothesis was that the “blocking” is entirely implemented in the front-end: that the JavaScript code that makes the pretty bits work is looking at the “country” data that’s returned and using that to decide whether or not to load the audio stream. That provides many different ways to bypass it, from manipulating the JavaScript to remove that functionality, to altering the JSON response so that every station appears to be in the user’s country, to writing some extra code that intercepts the request for the metadata and injects an extra audio player that doesn’t comply with the regional restrictions.

But first I needed to be sure that there wasn’t some actual e.g. IP-based blocking on the streams. To do this, first I took the /api/ara/content/listen/[ID]/channel.mp3 address of a known-working station and opened it in VLC using Media > Open Network Stream…. That worked. Then I did the same thing again, but substituted the [ID] part of the address with the ID of a “blocked” station. VLC happily started spouting French to me: the bypass would, in theory, work!

Next, I needed to get that to work from within the site itself. It’s implemented in React, which is a pig to inject code into because it uses horrible identifiers for DOM elements. But of course I knew that there’d be this tell-tale fetch request for the station metadata that I could tap into, so I used this technique to override the native fetch method and replace it with my own “wrapper” that logged the stream address for any radio station I clicked on. I tested the addresses this produced using my browser.

window.fetch = new Proxy(window.fetch, {
  apply: (target, that, args)=>{
    const tmp = target.apply(that, args);
    tmp.then(res=>{
      const matches = res.url.match(/\/api\/ara\/content\/channel\/(.*)/);
      if(matches){
        const stationId = matches[1];
        console.log(`http://radio.garden/api/ara/content/listen/${stationId}/channel.mp3`);
      }
    });
    return tmp;
  },
});

That all worked nicely, so all I needed to do now was to use those addresses rather than simply logging them. Rather that get into the weeds reverse-engineering the built-in player, I simply injected a new <audio> element after it and pointed it at the correct address, and applied a couple of CSS tweaks to make it fit in nicely.

The only problem was that on UK-based radio stations I’d now hear a slight echo, because the original player was still working. I could’ve come up with an elegant solution to this, I’m sure, but I went for a quick-and-dirty hack: I used res.json() to obtain the body of the metadata response… which meant that the actual code that requested it would no longer be able to get it (you can only decode the body of a fetch response once!). radio.garden’s own player treats this as an error and doesn’t play that radio station, but my new <audio> element still plays it perfectly well.

It’s not pretty, but it’s functional. You can read the finished source code on Github. I don’t anticipate that I’ll be maintaining this script so if it stops working you’ll have to fix it yourself, and I have no intention of “finishing” it by making it nicer or prettier. I just wanted to share in case you can learn anything from my approach.

× × ×

Dot-dash-diss: The gentleman hacker’s 1903 lulz

This is a repost promoting content originally published elsewhere. See more things Dan's reposted.

A century ago, one of the world’s first hackers used Morse code insults to disrupt a public demo of Marconi’s wireless telegraph

Nevil Maskelyne – doing it for the lulz?

LATE one June afternoon in 1903 a hush fell across an expectant audience in the Royal Institution’s celebrated lecture theatre in London. Before the crowd, the physicist John Ambrose Fleming was adjusting arcane apparatus as he prepared to demonstrate an emerging technological wonder: a long-range wireless communication system developed by his boss, the Italian radio pioneer Guglielmo Marconi. The aim was to showcase publicly for the first time that Morse code messages could be sent wirelessly over long distances. Around 300 miles away, Marconi was preparing to send a signal to London from a clifftop station in Poldhu, Cornwall, UK.

Yet before the demonstration could begin, the apparatus in the lecture theatre began to tap out a message. At first, it spelled out just one word repeated over and over. Then it changed into a facetious poem accusing Marconi of “diddling the public”. Their demonstration had been hacked – and this was more than 100 years before the mischief playing out on the internet today. Who was the Royal Institution hacker? How did the cheeky messages get there? And why?

An early example of hacking and a great metaphor for what would later become hacker-culture, found in the history of the wireless telegraph.

Note #10132

One off the #bucketlist this morning when local radio station @JACKfmOxford said, on-air, “Dan, please stop texting us puns.”

What Does Jack FM Sound Like?

Those who know me well know that I’m a bit of a data nerd. Even when I don’t yet know what I’m going to do with some data yet, it feels sensible to start collecting it in a nice machine-readable format from the word go. Because you never know, right? That’s how I’m able to tell you how much gas and electricity our house used on average on any day in the last two and a half years (and how much off that was offset by our solar panels).

Daily energy usage at Dan's house for the last few years. Look at the gas peaks in the winters, when the central heating ramps up!
The red lumps are winters, when the central heating comes on and starts burning a stack of gas.

So it should perhaps come as no huge surprise that for the last six months I’ve been recording the identity of every piece of music played by my favourite local radio station, Jack FM (don’t worry: I didn’t do this by hand – I wrote a program to do it). At the time, I wasn’t sure whether there was any point to the exercise… in fact, I’m still not sure. But hey: I’ve got a log of the last 45,000 songs that the radio station played: I might as well do something with it. The Discogs API proved invaluable in automating the discovery of metadata relating to each song, such as the year of its release (I wasn’t going to do that by hand either!), and that gave me enough data to, for example, do this (click on any image to see a bigger version):

Jack FM: Decade Frequency by Hour
Decade frequency by hour: you’ve got a good chance of 80s music at any time, but lunchtime’s your best bet (or perhaps just after midnight). Note that times are in UTC+2 in this graph.

I almost expected a bigger variance by hour-of-day, but I guess that Jack isn’t in the habit of pandering to its demographics too heavily. I spotted the post-midnight point at which you get almost a plurality of music from 1990 or later, though: perhaps that’s when the young ‘uns who can still stay up that late are mostly listening to the radio? What about by day-of-week, then:

Jack FM: Decade Frequency by Day of Week
Even less in it by day of week… although 70s music fans should consider tuning in on Fridays, apparently, and 80s fans will be happiest on Sundays.

The chunks of “bonus 80s” shouldn’t be surprising, I suppose, given that the radio station advertises that that’s exactly what it does at those times. But still: it’s reassuring to know that when a radio station claims to play 80s music, you don’t just have to take their word for it (so long as their listeners include somebody as geeky as me).

It feels to me like every time I tune in they’re playing an INXS song. That can’t be a coincidence, right? Let’s find out:

Jack FM: Artist Frequency
One in every ten songs are by just ten artists (including INXS). One in every four are by just 34 artists.

Yup, there’s a heavy bias towards Guns ‘n’ Roses, Michael Jackson, Prince, Oasis, Bryan Adams, Madonna, INXS, Bon Jovi, Queen, and U2 (who collectively are responsible for over a tenth of all music played on Jack FM), and – to a lesser extent – towards Robert Palmer, Meatloaf, Blondie, Green Day, Texas, Whitesnake, the Pet Shop Boys, Billy Idol, Madness, Rainbow, Elton John, Bruce Springsteen, Aerosmith, Fleetwood Mac, Phil Collins, ZZ Top, AC/DC, Duran Duran, the Police, Simple Minds, Blur, David Bowie, Def Leppard, and REM: taken together, one in every four songs played on Jack FM is by one of these 34 artists.

Jack FM: Top 20
Amazingly, the most-played song on Jack FM (Alice Cooper’s “Poison”) is not by one of the most-played 34 artists.

I was interested to see that the “top 20 songs” played on Jack FM these last six months include several songs by artists who otherwise aren’t represented at all on the station. The most-played song is Alice Cooper’s Poison, but I’ve never recorded them playing any other Alice Cooper songs (boo!). The fifth-most-played song is Fight For Your Right, by the Beastie Boys, but that’s the only Beastie Boys song I’ve caught them playing. And the seventh-most-played – Roachford’s Cuddly Toy – is similarly the only Roachford song they ever put on.

Next I tried a Markov chain analysis. Markov chains are a mathematical tool that examines a sequence (in this case, a sequence of songs) and builds a map of “chains” of sequential songs, recording the frequency with which they follow one another – here’s a great explanation and playground. The same technique is used by “predictive text” features on your smartphone: it knows what word to suggest you type next based on the patterns of words you most-often type in sequence. And running some Markov chain analysis helped me find some really… interesting patterns in the playlists. For example, look at the similarities between what was played early in the afternoon of Wednesday 19 October and what was played 12 hours later, early in the morning of Thursday 20 October:

19 October 2016 20 October 2016
12:06:33 Kool & The Gang – Fresh Kool & The Gang – Fresh 00:13:56
12:10:35 Bruce Springsteen – Dancing In The Dark Bruce Springsteen – Dancing In The Dark 00:17:57
12:14:36 Maxi Priest – Close To You Maxi Priest – Close To You 00:21:59
12:22:38 Van Halen – Why Can’t This Be Love Van Halen – Why Can’t This Be Love 00:25:00
12:25:39 Beats International / Lindy – Dub Be Good To Me Beats International / Lindy – Dub Be Good To Me 00:29:01
12:29:40 Kasabian – Fire Kasabian – Fire 00:33:02
12:33:42 Talk Talk – It’s My Life Talk Talk – It’s My Life 00:38:04
12:41:44 Lenny Kravitz – Are You Gonna Go My Way Lenny Kravitz – Are You Gonna Go My Way 00:42:05
12:45:45 Shalamar – I Can Make You Feel Good Shalamar – I Can Make You Feel Good 00:45:06
12:49:47 4 Non Blondes – What’s Up 4 Non Blondes – What’s Up 00:50:07
12:55:49 Madness – Baggy Trousers Madness – Baggy Trousers 00:54:09
Eagle Eye Cherry – Save Tonight 00:56:09
Feeling – Love It When You Call 01:04:12
13:02:51 Fine Young Cannibals – Good Thing Fine Young Cannibals – Good Thing 01:10:14
13:06:54 Blur – There’s No Other Way Blur – There’s No Other Way 01:14:15
13:09:55 Pet Shop Boys – It’s A Sin Pet Shop Boys – It’s A Sin 01:17:16
13:14:56 Zutons – Valerie Zutons – Valerie 01:22:18
13:22:59 Cure – The Love Cats Cure – The Love Cats 01:26:19
13:27:01 Bryan Adams / Mel C – When You’re Gone Bryan Adams / Mel C – When You’re Gone 01:30:20
13:30:02 Depeche Mode – Personal Jesus Depeche Mode – Personal Jesus 01:33:21
13:34:03 Queen – Another One Bites The Dust Queen – Another One Bites The Dust 01:38:22
13:42:06 Shania Twain – That Don’t Impress Me Much Shania Twain – That Don’t Impress Me Much 01:42:23
13:45:07 ZZ Top – Gimme All Your Lovin’ ZZ Top – Gimme All Your Lovin’ 01:46:25
13:49:09 Abba – Mamma Mia Abba – Mamma Mia 01:50:26
13:53:10 Survivor – Eye Of The Tiger Survivor – Eye Of The Tiger 01:53:27
Scouting For Girls – Elvis Aint Dead 01:57:28
Verve – Lucky Man 02:00:29
Fleetwood Mac – Say You Love Me 02:05:30
14:03:13 Kiss – Crazy Crazy Nights Kiss – Crazy Crazy Nights 02:10:31
14:07:15 Lightning Seeds – Sense Lightning Seeds – Sense 02:14:33
14:11:16 Pretenders – Brass In Pocket Pretenders – Brass In Pocket 02:18:34
14:14:17 Elvis Presley / JXL – A Little Less Conversation Elvis Presley / JXL – A Little Less Conversation 02:21:35
14:22:19 U2 – Angel Of Harlem U2 – Angel Of Harlem 02:24:36
14:25:20 Trammps – Disco Inferno Trammps – Disco Inferno 02:28:37
14:29:22 Cast – Guiding Star Cast – Guiding Star 02:31:38
14:33:23 New Order – Blue Monday New Order – Blue Monday 02:36:39
14:41:26 Def Leppard – Let’s Get Rocked Def Leppard – Let’s Get Rocked 02:40:41
14:46:28 Phil Collins – Sussudio Phil Collins – Sussudio 02:45:42
14:50:30 Shawn Mullins – Lullaby Shawn Mullins – Lullaby 02:49:43
14:55:31 Stars On 45 – Stars On 45 Stars On 45 – Stars On 45 02:53:45
16:06:35 Dead Or Alive – You Spin Me Round Like A Record Dead Or Alive – You Spin Me Round Like A Record 03:00:47
16:09:36 Dire Straits – Walk Of Life Dire Straits – Walk Of Life 03:03:48
16:13:37 Keane – Everybody’s Changing Keane – Everybody’s Changing 03:07:49
16:17:39 Billy Idol – Rebel Yell Billy Idol – Rebel Yell 03:10:50
16:25:41 Stealers Wheel – Stuck In The Middle Stealers Wheel – Stuck In The Middle 03:14:51
16:28:42 Green Day – American Idiot Green Day – American Idiot 03:18:52
16:33:44 A-Ha – Take On Me A-Ha – Take On Me 03:21:53
16:36:45 Cranberries – Dreams Cranberries – Dreams 03:26:54
Elton John – Philadelphia Freedom 03:30:56
Inxs – Disappear 03:36:57
Kim Wilde – You Keep Me Hanging On 03:40:59
16:44:47 Living In A Box – Living In A Box
16:47:48 Status Quo – Rockin’ All Over The World Status Quo – Rockin’ All Over The World 03:45:00

The similarities between those playlists (which include a 20-songs-in-a-row streak!) surely can’t be coincidence… but they do go some way to explaining why listening to Jack FM sometimes gives me a feeling of déjà vu (along with, perhaps, the no-talk, all-jukebox format). Looking elsewhere in the data I found dozens of other similar occurances, though none that were both such long chains and in such close proximity to one another. What does it mean?

There are several possible explanations, including:

  • The exotic, e.g. they’re using Markov chains to control an auto-DJ, and so just sometimes it randomly chooses to follow a long chain that it “learned” from a real DJ.
  • The silly, e.g. Jack FM somehow knew that I was monitoring them in this way and are trying to troll me.
  • My favourite: these two are actually the same playlist, but with breaks interspersed differently. During the daytime, the breaks in the list are more-frequent and longer, which suggests: ad breaks! Advertisers are far more-likely to pay for spots during the mid-afternoon than they are in the middle of the night (the gap in the overnight playlist could well be a short ad or a jingle), which would explain why the two are different from one another!

But the question remains: why reuse playlists in close proximity at all? Even when the station operates autonomously, as it clearly does most of the time, it’d surely be easy enough to set up an auto-DJ using “smart random” (because truly random shuffles don’t sound random to humans) to get the same or a better effect.

Jack FM Style Guide
One of the things I love about Jack FM is how little they take seriously. Like their style guide.

Which leads to another interesting observation: Jack FM’s sister stations in Surrey and Hampshire also maintain a similar playlist most of the time… which means that they’re either synchronising their ad breaks (including their duration – I suspect this is the case) or else using filler jingles to line-up content with the beginnings and ends of songs. It’s a clever operation, clearly, but it’s not beyond black-box comprehension. More research is clearly needed. (And yes, I’m sure I could just call up and ask – they call me “Newcastle Dan” on the breakfast show – but that wouldn’t be even half as fun as the data mining is…)

× × × × × ×

Poly In The Media

As I mentioned in my reflections on this year’s Valentine’s Day, I was recently interviewed by a media student putting together a radio documentary as part of her Masters thesis. She’d chosen polyamory as the subject of her documentary, and I met her in a discussion on social news website Reddit. I’d originally expected that the only help I’d be able to provide would be some tips on handling the subject – and the community – sensitively and without excessive sensationalism, but it later turned out that I’d be able to be of more aid than I initially expected.

I rarely get the chance to talk to the media about polyamory. I’m happy to do so – I’m registered with the Polyamory Media Association and I’ll sometimes reply to the requests of the (sensible-sounding) journalists who reach out to the uk-poly mailing list. However, I’m often not a suitable candidate because my partner (Ruth) and her husband (JTA) aren’t so poly-activist-ey as me, and don’t really want to be interviewed or photographed or to generally put into the public eye.

Logo of the Polyamory Media Association.

I respect that. It’s actually pretty damn sensible to not want your private life paraded about in front of the world. I’ve known people who, despite taking part in a perfectly good documentary about their love lives, have faced discrimination from – for example – their neighbours, subsequently. I appreciate that, often, reporters are challenged by how hard it is to find people who are willing to talk about their non-monogamous relationships, but it turns out that there’s a pretty-good reason for that.

From my perspective, I feel like it’s my duty to stand up and say, “I’m in an ethical, consensual, non-monogamous relationship… and I’m just another normal guy!” Jokes aside about how I’m perhaps not the best spokesperson to represent a “normal guy”, this is important stuff: people practicing ethical non-monogamy face discrimination and misunderstanding primarily because society often doesn’t have a reference point from which to understand that these people are (otherwise) perfectly normal. And the sooner that we can fix that, the sooner that the world will shrug and get on with it. Gay people have been fighting a similar fight for far longer, and we’re only just getting to the point where we’re starting to see gay role models as film and television characters for whom their sexuality isn’t the defining or most-remarkable part of their identity. There’s a long way to go for all of us.

Emily: the media student who interviewed us.

Emily – the media student who came over to interview me – was friendly, approachable, and had clearly done her homework. Having spoken online or by telephone to journalists and authors who’ve not had a clue about what they were talking about, this was pretty refreshing. She also took care to outline the basis for her project, and the fact that it was primarily for her degree, and wouldn’t be adapted for broadcast without coming back and getting the permission of everybody involved.

I’m not sure which of these points “made the difference”, but Ruth (and later, JTA) surprised me be being keen to join in, sitting down with Emily and I over a bottle of wine and a big fluffy microphone and chatting quite frankly about what does and doesn’t work for us, what it all means, how to “make it work”, and so on. I was delighted to see how much our answers – even those to questions that we hadn’t anticipated or hadn’t really talked about between ourselves, before – aligned with one another, and how much compatibility clearly exists in our respective ideas and ideals.

I was particularly proud of Ruth. Despite having been dropped into this at virtually no notice, and having not previously read up on “how to talk to the media about polyamory” nor engaged in similar interviews before, she gave some wonderfully considered and concise soundbites that I’m sure will add a lot of weight and value to the final cut. Me? I keep an eye on things (thanks, Polyamory In The News) and go out of my way to look for opportunities to practice talking to people about my lifestyle choice. But even without that background, Ruth was a shining example of “how to do it”: the kind of poly spokesperson that I wish that we had more of.

I hope that Emily manages to find more people to interview and gets everything that she needs to make her project a success: she’s got a quiet tact that’s refreshing in polyamory journalism. Plus, she’s a genuinely nice person: after she took an interest in the board games collection on New Earth, we made sure to offer an open invite for her to come back for a games night sometime. Hell: maybe there’s another documentary in there, somewhere.

× ×

Northern Radio

As I mentioned earlier, I spent some of the period between Christmas and New Year in Preston. And there, while taking a shower at my mother’s house, I had a strange experience.

My mother’s shower is one of the new style of high-tech ones, with a dozen different washing functions as well as a built in light and radio. I gather that there are ones with built in phones, now, too.

One of the funky features of my mother’s shower cubicle is that it includes a fully working FM radio. Its controls are pretty limited and there’s no user interface to provide feedback about what frequency you’re tuned to already, so it’s hard to deliberately tune in to a specific station. Instead, the house policy seems to be that if you don’t like what you’re listening to, you press the “cycle to the next station” button until you hear something you like.

Listening to music is about the third or second most-enjoyable thing that one can possibly do in a shower, in my experience, so I gave it a go. Local station Radio Wave came on, and they were playing some fun tunes, so I sang along as I washed myself under the hot steamy “drench” setting on the shower.

Radio Wave (96.5FM), Blackpool, Lancashire

At the end of a couple of songs, there were some commercials and the show’s presenter shared a few words. And it occurred to me quite how very Northern he sounded.

Living and working in Oxford, I don’t in my day to day life come across people with that broad lanky dialect. Growing up in Preston, and going to school there, I came across it on a daily basis, but didn’t notice it. Now, in its absence, it’s starkly noticeable, with its traditional short gutteral “t” instead of “the”, use of the archaic second-person “tha” (related to “thou”), and the ever-present pronunciation of words like “right” and “light” as “reet” and “leet”, and “cold” and “old” as “cowd” and “owd”.

It’s unfamiliar, but still “homely”. Like that smell that reminds you of where you grew up, this sound to my ears filled me with a strange nostalgia.

It’s funny, because I’m sure I carry a little bit of that accent with me. To the folks in my life around Oxford way, I perhaps sound as foreign as those people in Preston sound to me, now. I spoke on the phone the other week to a couple of people I used to hang out with, back in the day, and my immediate thought was that they’d become more Lanky than I remembered – as if they’d somehow overdosed on butter pie and barm cakes in the years since I last saw them.

But that’s clearly not the case: it’s not their voices that have changed, but my ears. Untouched by the North-Western tongue for so long, it sounds very strange to me now to hear it over the phone, on the radio, or even in person.

It’s a strange side-effect of moving around the country. I wonder what it’s like for my American friends, who have an even bigger gap (both geographically and linguistically) between their homes in the UK and their families in the US, to “phone home”.

× ×

The Crack

There’s a man in the house. He carries a hammer in his toolbelt and shows the crack of his bottom over the top of his worn workwear even when he’s not crawling around on the floorboards. He’s been sent to repair a few bits of Earth, our perpetually-falling-apart house, and to quote for a handful of further improvements that he’s hoping to persuade the landlord to let him install after we’ve gone.

He repairs the wobbly floorboard in my office while I try to get on with some work. The floorboard sinks considerably when it’s walked over, and feels like it might at any moment send me plummeting down into Paul‘s room. It’ll be good to have it repaired, even if this does occur only weeks before we are due to move out.

I’m listening to a Radio 4 program about disenchantment with contemporary financial establishments and cyber-trading and the recent growth of interest in gold trading as a “safety net”. A panellist says that for the first time in recorded history, the majority of gold is held by private investors, rather than by central banks. At some point, another panellist describes the expertise required by financial traders and a post-capitalist economy as being esoteric.

The builder pulls his head out from below the floorboards and speaks. “Ee-sow-terick?” he says, “I don’t even know what that means!”

“That’s subtly ironic, then!” I reply, not sure whether or not he’s being serious.

The builder makes a grunting sound that I interpret as being a derivation on the word “Huh?”

“Something esoteric is… something known only to a few; to an elite minority, perhaps,” I begin. “Like the word itself, it turns out,” I add, after a pause.

The builder grunts again; a sound that expresses his disinterest even more thoroughly than did his last utterance. He rolls the carpet back to where it belongs, and – by way of demonstration – jumps up and down. Somehow, in the last two minutes, he’s managed to repair the fragile floorboard. I didn’t even see what he was doing: one moment there was a hole in the floor, and now… everything was fine. I’d have been no less surprised if he’d produced the Nine of Spades from behind my ear. Perhaps I was merely distracted by the radio, but I’ve got no idea how he did it.

×