Writing a local Windows audio scrobbler
The practice of listening to MP3 files and maintaining an MP3 collection is certainly a thing of the past now. In fact, if we asked a few tech-savvy teenagers what an MP3 file is, some wouldn’t even know.
Services such as Spotify and Tidal have replaced manually searching for, downloading, and cataloguing music, much like Netflix and Disney+ have obliterated the pay-per-view and rental VOD market.
As with every technology out there, there are a few dinosaurs who refuse to give up old habits for various reasons, the author of this blog being one of them.
Having a private collection of files offers the same benefits as general local storage offers over cloud solutions: instant direct access, ability to edit and cut out content with ease, no need for an internet connection, and more.
Local scrobbling perfectly compliments local playback. Last.fm, Libre.fm, and the like are nice while they last, but they are not profitable and therefore can be discontinued any day.
By writing a local scrobbling application, we have full control over which tag fields are logged and how the file looks. It can then be backed up with ease.
In this article, we’re going to write a scrobbling application using Python for the Windows operating system. It will run continuously while we are logged in. It will listen to changes in Foobar playback and log those changes to a file according to our specified logic.
A few years ago, I wrote another article that does the same thing for graphical Linux, using the DeaDBeeF player and written in Bash. If you need a Linux version, here is a link to the article.
Requirements
- Python. Python. This ever-popular programming language is a first choice when it comes to many tasks. Due to its simplicity and readability, it is certainly a joy to write in compared to most other languages.
Download the latest Python version from the official Python website here.
- foobar 2000. One of the easiest ways to make a local audio scrobbling script is to monitor an audio player’s title string, extract data from it, manipulate it, and store it in a file. There aren’t that many MP3 players for Windows, let alone ones that allow us to manipulate their title. Of the very few, foobar2000 is the best, so it is our choice.
Here is a link to the download section of the official foobar2000 website.
Configuring Foobar 2000
The version I am using as of the time of writing this article is 2.1.4.
Go to Preferences by either pressing Ctrl+p or through File -> Preferences. In the left menu tree select Display, then select Default User Interface under it.
In the Playback state display formatting section, you need to edit the Window title field. Replace whatever you find there with:
%artist% -.- %album% -.- %title% -.- %date% -.- %length% -.- %playback_time%
As you can see, foobar2000 has many different tags encapsulated between percentage signs. Putting ‘ -.- ‘ is our choice. A distinct combination of strings has to be used so that the script can later extract data between them. Using the selected solution generally guarantees this. There is a very low chance that this particular string is going to appear in artist, album, or title tags.
On the other hand, it doesn’t look horrible (although it’s certainly not pretty), which is also important because if our Windows taskbar is configured to show icons and program titles instead of just icons, we are going to be looking at it every time the artist tag field of the currently played song in foobar2000 is short enough.
Breaking apart the script
Let’s get down to it.
The imports section is quite straightforward, befitting a miniscule script.
PyGetWindow is the only module which is not built-in. Thanks to it, we get access to titles of all programs that are currently running. Download it from the command line with this command:
pip install pygetwindow
Next, we define 3 functions to aid us in the audio scrobbling process:
foobar2000 adds the string ’ [foobar2000]’ at the end of the program title, and it’s non-negotiable. Therefore, we have a convenient string to search for in order to isolate foobar2000 from other applications and get the playback data.
This function uses PyGetWindow to iterate through all the currently opened window titles and give us foobar2000.
In the script, playback time is important because we’re going to be constantly comparing the previously stored time with the current one in order to detect if the track has changed. To effectively do this comparison, it’s easiest to convert minutes to seconds by multiplying the value representing minutes by 60 and adding it to the value representing seconds.
Once we get our title, there are a few things that need to be done with it:
First, our ’ -.- ‘ separator needs to be replaced with the CSV separator needed for the scrobbling file. ’;’ is a considerably safer choice than a comma, because commas are very frequent in tags while semicolons are not.
Next, there needs to be a timestamp at the beginning of each scrobble to indicate when that scrobble took place. Python’s datetime module provides a simple solution for this.
After we get the datetime object, we format it according to a practical specification. Year is followed by month, which is then followed by day, then after a semicolon we get the current hour and minute of the day. A semicolon at the end prepares the string for the actual tag stripe.
Finally, we add the timestamp to the tag content, remove unnecessary stuff from the end of the string, and return the complete scrobble line.
After defining functions, we form starting variables:
csv_file_location needs to be set to a path on your drive where the scrobbling file is to be stored.
Setting up time_played_past and time_played_current is just priming the pump before the action starts. In other words, we set up initial values for the script to have something to work with.
time_played_past is needed once when the script initiates. For playback logging to be registered, the variable stores 9999 “seconds” so when we begin playback, time_played_current has a lower number. This means a track has been changed - if the same track were still playing, that number would go up, not down.
The number going down indicates changing of the track or restarting the currently played track. One way or another, it is a sign to add that new item to our scrobble history.
We use while True: for the contained code to run infinitely. We want the script to be always on and always listening for new playback changes.
Because the code is simple and extremely lightweight, it doesn’t slow down the system by any noticeable amount. In fact, it probably wouldn’t even if we were running it 500 times in parallel.
The script begins to move by checking if among all currently opened windows there is one whose title contains the string representing foobar2000.
Just to make sure that we don’t have accidentally opened a website or a document which contains that very string and could fool our script, we add a second condition for further execution - the string has to contain our chosen tag separator in foobar2000. A situation where both of these conditions are met and the string captured is still not a foobar2000 player is probably as improbable as winning the national lottery five times in a row ;)
As explained before, in order to compare current playback time with the last tick, we need to feed time to a function that does just that. For that, we first extract the part of the string which contains playback time and then extract it from that line.
Making changes to a scrobble file is guarded behind a condition that the currently played file plays for fewer seconds than our last stored state.
When the condition is met, we use the function that converts the foobar2000 title to a final CSV-ready stripe (line). After that, we append that stripe to our file whose location is stored in csv_file_location.
The program cycle then needs two more actions to complete.
First, the variable time_played_past needs to get the value of time_played_current. In other words, our variable to which we will compare the next playback state needs to be updated.
Finally, we order the script to sleep for 3 seconds. The amount of time which is best depends on the habits of a particular user. One person changes tracks 20 times per minute, another presses play after adding a big playlist and never tinkers with playback until the program is shut down. The value must be chosen depending on your habits.
Final word
By following this guide, you’ve gained the tools to create a custom local audio scrobbler for Windows, empowering you to maintain full control over your music collection and playback data. This setup ensures your scrobbles are logged accurately and reliably, without dependency on external services. As you continue to enjoy your personalized music experience, feel free to tweak and expand the script to better suit your needs. Thank you for reading, and happy scrobbling!
Below is the code for the entire script. If you don’t know how to make the script run permanently in the background in Windows, here is an article that guides you through the process.