Introduction
Hello! My name is Rimma Kubanova (AKA rimskii on IRC and rimma-kubanova on GitHub). I’m an undergraduate Computer Science student at Nazarbayev University in Astana, Kazakhstan. My inspiration to participate in Google Summer of Code came from seeing my seniors’ experiences. I began contributing to MetaBrainz because I felt their goals and technologies aligned perfectly with my interests and skills.
After making my first contributions, I decided to apply to GSoC, and to my delight, my proposal was accepted!
Proposal
ListenBrainz generates music recommendation playlists based on a user’s listening history and habits. These playlists can be enjoyed directly in ListenBrainz and automatically exported to the user’s Spotify account. However, currently, ListenBrainz only supports exporting to Spotify, which limits the user experience.
My project focused on expanding this functionality by integrating support for exporting these playlists to other external music services like SoundCloud and Apple Music. Additionally, I proposed adding an import feature to allow users to bring their playlists from these services into ListenBrainz.
My proposal can be found here.
Project Overview
My project focused on adding both exporting and importing features to user playlists in ListenBrainz.
Users can import playlists from any music service they’re authorized with, selecting which playlists to import. Additionally, for each playlist in ListenBrainz, there is an export feature that works automatically and is available if the user is authenticated with the respective music service.
The project consisted of two parts: ListenBrainz and the Troi recommendation engine.
Troi is the core engine that manages most of the playlist-related tasks in ListenBrainz, including music recommendations and algorithmic playlist generation. It handles playlist and track processing, generates playlists, and manages exporting or importing as needed. Meanwhile, ListenBrainz is responsible for receiving user data from the frontend, processing it on the backend, and coordinating with Troi.

Pre Community Bonding Period
I began contributing to this project starting from January 2024, well before the official community bonding period. During this time, I implemented a system for importing playlists from Spotify, including creating web components, and developing new endpoints. I also tackled various tickets in Jira. These early contributions allowed me to become deeply familiar with the codebase and MetaBrainz’s workflow even before submitting my GSoC proposal. You can find my early contributions here.
When the program began, the community bonding period provided a great opportunity to get to know my mentors and fellow GSoC participants better. It allowed me to discuss key aspects of my project with my mentor and prepare thoroughly before starting the actual development work.
Coding Part: Importing Playlists
During the first part of my project, I focused specifically on developing the import features. Before the program officially began, I had already implemented the functionality for importing playlists from Spotify into ListenBrainz. However, upon @lucifer’s advice, I restructured the system to move the core logic into the Troi recommendation engine. This change resulted in faster code execution and more flexibility, allowing the code to be used beyond just ListenBrainz.

The playlist importing functionality follows the sequence mentioned above:
- Listenbrainz. In these interactions, the LB server manages user requests, specifically
get_user_playlists
andget_user_playlist_tracks
endpoints. Theget_user_playlists
endpoint gets user playlists from all supported music services into a single interface, while theget_user_playlist_tracks
functionality is split into multiple endpoints, to meet the specific requirements of different music service APIs. This design choice enhances code readability, scalability, and ensures that all processing occurs on the backend.
PR for Spotify, SoundCloud and Apple Music can be found here. - In Troi, I developed a new patch called
ImportPlaylistPatch
, which is responsible for retrieving existing playlists from music services for use in troi. Troi utilizes a pipeline architecture, and the process works as follows: it fetches tracks from the specified playlists, calls Labs API to convert the giventrack_name
,artist_name
andrelease_name
into the corresponding MBID (MusicBrainz track ID), adds them to the playlist, and then uploads to ListenBrainz. The code for the process can be seen below:
//in troi/patches/playlist_from_ms.py
//fetch tracks from music services and convert to mbid
source = RecordingsFromMusicServiceElement(token=ms_token, playlist_id=playlist_id, music_service=music_service)
//lookup for musicbrainz data based on mbid
rec_lookup = RecordingLookupElement()
rec_lookup.set_sources(source)
//create a playlist
pl_maker = PlaylistMakerElement(name, desc, patch_slug=self.slug())
pl_maker.set_sources(rec_lookup)
//in troi/import_ms.py
//generate playlist and upload to listenbrainz
patch.generate_playlist()
My PRs for Spotify, SoundCloud and Apple Music.
The design for these components was chosen and discussed with @aerozol. It was also recommended to implement the modals separately to allow for easier scaling and the addition of more features in the future.


Limitations: SoundCloud stores track information differently, often including extra symbols in titles, embedding artist names within the track title, and frequently listing publishers that don’t match the actual artist. This made it difficult to automatically import all SoundCloud tracks. To address this, we developed a system to extract the artist name and track name from the title, but unfortunately, it doesn’t work perfectly in all cases.
//troi/tools/soundcloud_lookup.py
mapped_tracks = [
{
"recording_name": track['title'].split(" - ")[1] if " - " in track['title'] else track['title'],
"artist_name": track['title'].split(" - ")[0] if " - " in track['title'] else track['user']['username']
}
for track in tracks
]
Coding Part: Exporting Playlists

The playlist export functionality follows this sequence:
- Listenbrainz. LB uses troi for exporting playlist, sends all information to troi, including playlist_id, user_token, and music service related information and calls TransferPlaylistPatch.
PRs for SoundCloud and Apple Music. - Troi. The design involved retrieving the JSPF (MusicBrainz playlist transport format) or the playlist MBID, converting them into Playlist element in Troi, using a track lookup through the database to find track IDs, and exporting them to the user’s account on the selected music service.
//in troi/patches/playlist_from_listenbrainz.py
PlaylistFromJSPFElement(playlist_mbid=mbid, jspf=jspf, token=token)
//convert playlist JSPF to troi element
_deserialize_from_jspf(data)
//troi/playlist.py
//generate playlist
generate_playlist(self):
//submit to specified music_service
submit_to_spotify() || submit_to_soundcloud() || submit_to_apple_music()
- Track Lookups. Finding the track IDs for music services based on MusicBrainz tracks was a significant challenge. To address this, we developed lookup endpoints in the Labs API. The system matched the given track and artist names and retrieved the corresponding track IDs. While multiple track IDs could be found for a given track, we opted to use the first match.
Endpoints implemented:
Apple Music:
https://labs.api.listenbrainz.org/apple-music-id-from-mbid
https://labs.api.listenbrainz.org/apple-music-id-from-metadata
SoundCloud:
https://labs.api.listenbrainz.org/soundcloud-id-from-mbid
https://labs.api.listenbrainz.org/soundcloud-id-from-metadata - Unplayable Tracks: Not all tracks could be exported successfully, often due to regional restrictions. To mitigate this, we implemented an alternative track-finding option that used other available track IDs found through the lookup process, which significantly improved the success rate of exported tracks.
Here is the example with SoundCloud:
//in troi/tools/soundcloud_lookup.py
//fixes unplayable tracks in the given soundcloud playlist.
def fixup_soundcloud_playlist(soundcloud: SoundcloudAPI, playlist_id: str, mbid_soundcloud_id_idx, soundcloud_id_mbid_idx):
//look for unplayable tracks, sort them out
playable, unplayable = _check_unplayable_tracks(soundcloud, playlist_id)
if not unplayable:
return
//look for alternatives
alternative_ids, index = _get_alternative_track_ids(unplayable, mbid_soundcloud_id_idx, soundcloud_id_mbid_idx)
if not alternative_ids:
return
//if found, resubmit them to playlist
fixed_up = _get_fixed_up_tracks(soundcloud, alternative_ids, index)
Here are PRs for SoundCloud and Apple Music.
MusicKit Limitations: MusicKit, the primary library for the Apple Music API, had several limitations, such as not being able to update playlists or remove tracks. This restricted our approach to finding alternative tracks. As a result, we decided not to include this feature for Apple Music and instead updated the code to create a new playlist each time, rather than updating an existing one.
Additionally, based on my mentors’ recommendations, I focused on optimizing API calls. Since most of my project involved working with APIs, it was crucial to optimize them as much as possible. I refactored API calls to batch data transmission, added pagination support for SoundCloud callbacks. To manage rate limits, I also created a create_http_session
– method to reuse rate limiting configuration across the codebase.
def create_http_session():
""" Create an HTTP session with retry strategy for handling rate limits and server errors. """
retry_strategy = Retry(
total=3,
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=["HEAD", "GET", "OPTIONS"],
backoff_factor=1
)
adapter = HTTPAdapter(max_retries=retry_strategy)
http = requests.Session()
http.mount("https://", adapter)
http.mount("http://", adapter)
return http
To streamline the process, I added Apple Music API and SoundCloud API classes in Troi, which are fully responsible for all API callbacks related to these music services. These classes can be found here.
Current State
All import and export features have been implemented and deployed to production in Troi. In ListenBrainz, all features are also complete, except for the SoundCloud export and import, which are currently undergoing testing and revisions. The pull request for these changes is available here.
As of now, all features outlined in the proposal have been successfully implemented.
Future plans
In the future, I plan to enhance the code by addressing several key areas:
- I observed that about 10% of tracks fail to import successfully. To address this, I intend to implement a feature that detects unplayable tracks and searches for viable alternatives.
- Importing large playlists can take over 30-40 seconds, leading to unnecessary delays for users. To improve the user experience, I plan to add background processing for playlist imports, allowing the process to run asynchronously without making users wait.
- I also plan to add comprehensive unit tests to better handle errors and ensure the system functions correctly, minimizing the risk of issues in production.
Experience ☀️
This summer has been an incredible journey, and I’m deeply grateful to my team at MetaBrainz and the Google Summer of Code organizers. Throughout this experience, I’ve had the unique opportunity to contribute to open source and work on real-life projects. It’s rewarding to see my work on playlist importing and exporting now live in production!
I also gained experience with new technologies like Docker, improved my Git handling, deepened my understanding of API integration, and learned how to design systems for complex functionalities.
I am especially thankful to my mentor Kartik Ohri (@lucifer), who guided me patiently at every step of my project. I also want to express my gratitude to Robert Kaye (@mayhem), Monkey Do (@monkey) and Ansh Goyal (@anshg1214) for reviewing my PRs, and to @aerozol for assisting me with design aspects.
Meeting such incredible people at MetaBrainz and being offered this amazing opportunity has been a highlight of my journey!
(づ ◕‿◕ )づ