Detecting explicit content in album art: a lesson in proving concepts early

As I was developing StreamerSong.Cards, there was a certain feature that I wanted to add. If you aren't already familiar, StreamerSong.Cards is a web app that allows users (Twitch music streamers) to create custom themed overlays that display details about the song at the top of their song request queue. Album art was always a planned feature, but what I really wanted was to offer users an automated solution to save them from having to manually assign art for each song on their list.

I set about finding a suitable source of data and came upon Musicbrainz, a enormous, non-profit run collaborative database that is quite possibly the most comprehensive source of music metadata on the internet, where I was able to find a dataset that contained a list of all the albums in the database, as well as the data records of all album art images (though not in the same table). After spending a few days learning enough about PostgreSQL to merge the desired data into a single table, I made a script to assemble URLs from the image data.

Upon running some test queries and inspecting the images that were returned, I realized that I had a problem: album art is not always safe for work. Or Twitch...

There were some issues with my new data set itself (it is a bohemoth of a database that has been running for decades, so the structure is...labyrinthian), but I had bigger fish to fry so I filed those away as a mental ticket and got to work firguring out how to screen the images.

The most obvious solution was to use a service like Google Cloud Vision to analyze the images and flag any that were deemed inappropriate. So I got the Google Cloud cli set up on my machine and wrote a script that would allow me to feed it a list of artists, find all of the art available for each, screen each image until a safe one is found and store the results in a database. I ran the script on a few hundred images and at first, everything seemed like it was working. Great! Task done. Now I can deal with that dirty data.

I spent a week or so cleaning up the data, and once I was satisfied that it was as clean as it was going to get, I ran the script again on a larger set of images. It hummed along, spitting out images that didn't pass the test, and I went to look at my nice database of clean images. I looked for some of the usual suspects: Prince, Madonna, Britney Spears, etc. and was pleased to see that risque options had been replaced with more appropriate images.

I made a crude panel to display the results, and that's when I noticed a big, big problem. There were a lot of images that should not have passed the test. Taking a list of known explicit images and running it through Google Vision, there was a false negative rate of over 2%. I knew it was over. It was simply not a viable solution. I could try to run the set through other services (Amazon has a similar solution), but it was quite expensive and I would still have to manually screen the results for tens of thousands of images.

I was disappointed, realizing right away that, had I bothered to validate the Google Vision solution before carrying onward, I could have saved a lot of time.

My eventual fix was to replace the automated art system with a manual one, and I'm incredibly happy with how that turned out. Dreading the prospect of giving users an insurmountable task (some users have hundreds, if not thousands of songs in their list), I focused heavily on making the process as painless as possible and made an interface that displays all of the artist's albums when the user clicks on a song, and allows them to simply drag the image onto the song to assign it. This required a lot of work itself, but I'm very happy with the resulting experience. I had my partner/bandmate test it on our own song list and she didn't give my computer back to me for over an hour, not because it took that long, but because she had started playing with it.

Image of the art manager on StreamerSong.Cards

My main takeaway from this experience ended up being that I should take time to assess possible critical failure points early, set specific design requirements for complex features, and make sure that I thoroughly prove concepts before I get too deep in the weeds. This issue wasn't the end of the world, but it did cost me far more time than it should have.

StreamerSong.Cards will be moving into general availabililty soon, check out the new demo on the homepage!