GSoC 2025: Integrate music streaming from Funkwhale & Navidrome

Hey Everyone 👋🏻 !

I am Mohammad Amanullah (AKA m.amanullah7 on IRC and mAmanullah7 on GitHub) final year student at National Institute of Technology Agartala, India and along with that I am a diploma level student at Indian Institute of Technology Madras, India. I was thrilled to be selected as a contributor in the Google Summer of Code (GSoC) 2025 program. My project focused on integrating music streaming from Funkwhale & Navidrome. It was mentored by Lucifer and Monkey.

Let’s start 🙂

Project Overview

ListenBrainz has a number of music discovery features that use BrainzPlayer to facilitate track playback. BrainzPlayer (BP) is a custom React component in ListenBrainz that uses multiple data sources to search and play a track. As of now, it supports Spotify, Youtube, Apple Music and Soundcloud as a music service. It would be useful for BrainzPlayer to support stream music web apps like Navidrome and Funkwhale so that users could stream their private collections as well on ListenBrainz. For those unfamiliar, Funkwhale and Navidrome are self-hosted music servers that implement the Subsonic API, a widely adopted standard for streaming and managing personal music libraries.

Before you proceed further, listen to a song and explore new services so you can feel more when you read the rest of the blog! Check out your Connect services page 🎶

Let’s Deep Dive into My Coding Journey!

Welcome back to blog after exploring new services and listening to a song. I started my coding journey during the community bonding period. In this period, I spent time exploring, learning, discussing and creating the mockups, finalizing the backend and frontend flow so on with aerozol, monkey and lucifer!

The UI that we finally decided upon after many iterations!

Integrate music streaming from Funkwhale

Funkwhale supports OAuth2 in addition to Subsonic API. As OAuth2 is more secure, I decided to integrate Funkwhale using OAuth2. The main challenge here, unlike centralized services (Spotify, Apple Music), is that any user can host their own Funkwhale instance. Each server acts as its own OAuth provider and an app needs to be created for it dynamically. The following flowchart explains the various steps taken to connect a Funkwhale server to ListenBrainz.

Flowchart of connection Funkwhale server to ListenBrainz

The initial database schema had a single table which included both the server details (host_url, client_id, client_secret) and the token details (access_token, refresh_token, token_expiry). This was problematic as either the server app details would need to be duplicated for multiple users or a new app would be created for each user. Hence in the next iteration, I split it into two tables: funkwhale_servers, funkwhale_tokens to avoid redundancies.

Database schema for Funkwhale auth tables
CREATE TABLE funkwhale_servers (
    id                  INTEGER GENERATED ALWAYS AS IDENTITY,
    host_url            TEXT NOT NULL UNIQUE,
    client_id           TEXT NOT NULL,
    client_secret       TEXT NOT NULL,
    scopes              TEXT NOT NULL,
    created             TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE TABLE funkwhale_tokens (
    id                  INTEGER GENERATED ALWAYS AS IDENTITY,
    user_id             INTEGER NOT NULL,
    funkwhale_server_id INTEGER NOT NULL,
    access_token        TEXT NOT NULL,
    refresh_token       TEXT NOT NULL,
    token_expiry        TIMESTAMP WITH TIME ZONE NOT NULL
);
Flowchart explaining how a song is played through Funkwhale in BrainzPlayer

I added the option to connect a Funkwhale server in Connect services, the user needs to only input the url of their server.

Note: Funkwhale currently has a bug in the authentication workflow. If you are not logged into your Funkwhale server, before pressing “Connect to Funkwhale” rather than being redirected to the login page, you will be presented with an authentication error! You can look at this issue for more details!

UI Before connecting Funkwhale
UI once Funkwale is conencted and ready for playback

I added the FunkwhalePlayer component to BrainzPlayer by taking reference from the existing BrainzPlayer architecture and DataSourceType interface to understand how to properly integrate with the existing services. It detects when a listen originates from Funkwhale, handles both direct track URLs and search based matching, and manages authenticated audio streaming.

I also created a custom icon component for both Funkwhale and Navidrome that emulates the FontAwesome icons exported from react-fontawesome. It helps us to avoid messy and hardocded styles for the icon.

export const faFunkwhale: IconDefinition = {
  prefix: "fab" as IconPrefix,
  iconName: "funkwhale" as IconName,
  icon: [512, 512, [], "", [
    "M138.7 135.3c12.6 6.5 26.1 7.7 38.3 14.8...", // Whale body path
    "M223.2 284.9c35.9 0 65.1-29.2 65.1-65.1..."  // Sound wave arcs
  ]],
};

Once connected, ensure that Funkwhale service is activate and set the desired in the BrainzPlayer settings page.

All set! You are ready to play your Funkwhale collection on ListenBrainz! 
Enjoy 🎶


One of the complex parts was handling Funkwhale’s multi-artist format variations, Funkwhale’s track search API does not work well if we try to filter tracks by artist name. Hence, we only filter by track name in the API request and manually filter the results on the artist name. The track matching algorithm was particularly complex because Funkwhale supports multiple artist credit formats. Some tracks use the legacy single artist format and some newer instances use multi-artist credits with joinphrases. I implemented a normalization system that handles both the formats and also removes the accents for better matching, and uses a fallback strategy, exact artist as well as title match first, then artist filtered results, then any playable track.

For audio streaming, Funkwhale requires an access token. I implemented a system that fetches the audio as an authenticated blob and creates an object URL, and feeds that to the HTML5 audio element. I also implemented automatic token refresh so that the user does not experience interruptions in playback.

Present Status and Future Improvements

The entire implementation for the Funkwhale integration is contained in the following PR: Integrate music streaming from Funkwhale. The PR has already been reviewed, merged and deployed to proudction.

As future improvements, it would be useful to allow users to connect multiple funkwhale servers to their ListenBrainz account. More thorough unit and integration tests would also be useful in preventing regressions.

Integrate music streaming from Navidrome

Navidrome does not support OAuth2 currently. Effort is underway to add API Key auth support but as of now the the insecure Subsonic Auth is the only viable option. It involves storing the user’s password safely in the database and then using the md5(password+salt) as a token for authentication.

Flowchart detailing how to connect Navidrome to ListenBrainz.

Storing passwords in cleartext in the database is not safe, hence I used Fernet (symmetric encryption). The user’s password is encrypted before storage and only decrypted using the key when needed to generate API authentication tokens. This ensures that even if the database is compromised, the passwords remain secure. Following a similar pattern as Funkwhale created two tables, we can reuse navidrome_servers so we don’t need to save server url for each time and can also be useful when we upgrade this to store OAuth ids, tokens, scopes in future.

Database schema for Navidrome auth
CREATE TABLE navidrome_servers (
    id       INTEGER PRIMARY KEY,
    host_url TEXT UNIQUE
);
CREATE TABLE navidrome_tokens (
    user_id             INTEGER,
    navidrome_server_id INTEGER,
    username            TEXT,
    encrypted_password  TEXT -- Fernet-encrypted password
);

The frontend implementation followed the same DataSourceType pattern as Funkwhale and other music services, but there was a major difference in how we are handling authentication. Instead of maintaining an access token like an OAuth based implementation, the player generates fresh MD5 authentication parameters every time for each API request using the user’s stored credentials in the database.

Flowchart of playing a song using Navidrome in ListenBrainz

Connecting to a navidrome server requires three user inputs, host_url, username and password unlike funkwhale which only has host_url. Users can also edit the credentials later.

UI Before connecting
UI once Navidrome is connected

As with Funkwhale, you can activate Navidrome playback and set its priority in the BrainzPlayer settings page

All set! You are ready to play your Navidrome collection on ListenBrainz! 
Enjoy 🎶

The track matching was more straightforward for Navidrome as the Subsonic API provides a better search endpoint. The search3 endpoint allows us to query by both track and artist name simultaneously, and also return simple well structured results that are easier to parse than Funkwhale multi-format artist credits.

Navidrome track search
const searchForNavidromeTrack = async (
  instanceURL: string, authParams: string, trackName?: string, artistName?: string
): Promise<NavidromeTrack | null> => {
  
  const query = `${trackName || ""} ${artistName || ""}`.trim();
  const searchUrl = `${instanceURL}/rest/search3?query=${encodeURIComponent(query)}&songCount=1&${authParams}`;

  const response = await fetch(searchUrl);
  const data = await response.json();
  
  const searchResult = data["subsonic-response"]?.searchResult3;
  
  // Return first matching track
  return searchResult?.song?.[0] || null;
};

Audio streaming was significantly simpler than Funkwhale because Navidrome’s stream URLs include authentication parameters directly in the query string. This means I could set the HTML5 audio element’s src directly to the authenticated stream URL without needing to fetch the audio as a blob.

Current Status and Future Improvements

The Navidrome integration code is contained within the following PR: Integrate music streaming from Navidrome. It is currently pending review, following which it will be merged and deployed soon.

As future improvements, OAuth2 or API Key auth support can be added for Navidrome once available. The ability to connect multiple servers and more tests would be useful as well.

Testing

It was my first timing writing tests but I was successfully able to write basic tests for frontend as well as backend. The existing tests helped were easy to read and served as a great reference! In the future, I will add more functional tests.

Overall GSoC Experience

This summer has been an incredible journey for me to work with the MetaBrainz, and I’m deeply grateful to GSoC for this amazing opportunity. Contributing to ListenBrainz and implementing both Funkwhale and Navidrome music service integrations has been both challenging and rewarding, seeing my work now live in production for users worldwide. Being a part of MetaBrainz is an incredible feeling. I’m gonna miss Monday meetings for sure. I will be continuing to fix bugs, issues or contribute other improvements.

Throughout this journey, I have learned so many things. I am now more comfortable with Git ang Github. Initially, I did’t have much TypeScript knowledge. But, in this period, I worked on my skills, tried – failed – asked for help when stuck and finally finished the implementation. I have become more comfortable with Docker now and stuff like OAuth2 integration and music streaming implementation etc.

I would like to thanks monkey, lucifer, aerozol, a lot for helping me throughout this period, guiding me and for constantly support me. Whether it was the MetaBrainz chat or the PR reviews, I always received detailed feedback, help and suggestions. NGL monkey, I was sure I wouldn’t gonna help to create the Navidrome Icon but God had other plans.

I built some cool stuff this summer and it’s going to be used by people all over the world. Thank you to all others who helped me throughout this journey and helped and guided me! I hope you guys will enjoy listening to songs more with more services.

My proposal can be found here , PDF here
All my pull requests, commits for ListenBrainz during GSoC 2025!

Welcome Summer of Code 2025 contributors!

We are thrilled to announce the selection of 6 contributors to work with us for this year’s Google Summer of Code program! 

MetaBrainz received many great applications this year. Selecting the final contributors was tough and involved deliberating various factors – what these contributors did right is getting in early, engaging with our community, presenting specific and detailed proposals, and proving excellent communication skills and the ability to integrate our feedback back into their proposals.

Thank you to all contributors who submitted a proposal with us!

The whole list of selected proposals can be found on the GSOC website but here is a TL;DR breakdown:

MetaBrainz proposals

Matrix Archiver (libretto) (Jade Ellis AKA JadedBlueEyes)

This project proposal replaces BrainzBot with a new archival service that archives messages directly from Matrix to HTML files on disk and a PostgreSQL database. It will support Matrix features like message editing, reactions and media, and provide full text search over all messages. Both historical and new messages as they come in will be archived.

Centralized Notification System for MetaBrainz (Junaid AKA fettuccinae)

MetaBrainz contains multiple sub-projects which sends out standalone notifications. This project aims to centralize those by developing a shared notification system within metabrainz-org, enabling all sub-projects to deliver user notifications through this notification system. Expected Outcome: A functional notifications system with relevant API endpoint.

ListenBrainz proposals

Importing Listening History Files in Listenbrainz (Suvid Singhal)

This project aims to develop a feature that enables users to import their listening history from various services, including ListenBrainz exports, Spotify, Apple Music, and other CSV file formats. The proposed solution involves creating a backend API endpoint to handle file uploads, building normalizers to parse and validate data from different services, and converting the data to the JSONL format required by ListenBrainz. The solution also includes a frontend to handle file uploads and show the progress to the user.

Onboarding Revamp in Listenbrainz-Android (Hemang Mishra)

A smooth and intuitive onboarding experience is essential for any app, ensuring that users understand its features while maintaining trust and engagement. This project focuses on enhancing the ListenBrainz Android onboarding flow by making it more informative, user-friendly, and privacy-conscious. Key improvements include a dedicated Listen Submission screen to give users full control over which apps contribute listens, clear permission rationales, and fallback mechanisms for denied permissions. Additionally, a revamped sign-in screen will provide better navigation, including a bug report option for easy issue submission.

Development of Advanced User Statistics Visualizations (Granth Bagadia AKA holycow23 AKA granth23)

The project aims to design and implement advanced interactive visualizations for ListenBrainz using Nivo for data visualization and integrating with the existing Flask API. Apache Spark will handle efficient data processing and aggregation. These visualizations will offer granular insights into genre trends, artist diversity, and temporal listening patterns, enhancing user experience and engagement. The project will result in the development and integration of the following four interactive charts into ListenBrainz: Artist Listening, Activity Statistics, Listens by Era Statistics, Genre-Based Listening Patterns and Top Listeners.

Integrate music streaming from Funkwhale & Navidrome (Mohammad Amanullah AKA mAmanullah7)

Allow users to play music from their Funkwhale servers as well as Navidrome directly in BrainzPlayer, as both are self hosted music streaming platforms. Funkwhale used a OAuth2 for secure and safe authentication, but currently Navidrome used basic subsonic authentication (username/password + salt), but soon OAuth2 authentication also will be available for Navidrome. Once these are availanble, we can support Subsonic streaming in the ListenBrainz Player.

What if you’re not in GSoC 2025?

Reading this and feeling inspired for contributing to the code still? Volunteer contributors are very welcome all year round even though we might have slightly less time available to help you during the summer. It is also putting you in an ideal situation for applying to next year’s GSoC. You can find some tips for applying to GSoC with us in one of our previous posts. When you are ready, join us on the MetaBrainz Matrix Channel and showcase your initiative and your skills !

MetaBrainz Summit 2024

MetaBrainz nerds at the Jantar Mantar observatory. Left to right: jasje, reosarevok, atj, zas, KasukabeDefenceForce, monkey, yvanzo, lucifer, mayhem, ansh, theflash_, kellnerd, bitmap, akshaaatt, ApeKattQuest, outsidecontext, aerozol

This year it was New Delhi, India, that was invaded by data nerds from across the globe!

The MetaBrainz team was treated to the glorious chaos, hospitality, sights, noise, sweets, monkeys, traffic, heat, and delicious food of India. We reflected on the last year in MetaBrainz, planned and collaborated for the future, and got a little work done – when we could fit it in between mouthfuls of Indian sweets.

Read on for a comprehensive summit recap, including the annual recap for each MetaBrainz project, as well as breakout session notes, photos, and links to the slides and video recordings.

Continue reading “MetaBrainz Summit 2024”

You are invited to MetaBrainz Summit 24

MetaBrainz Summit 24 is upon us! September 23-27 in New Delhi, India.

We would love for you to join us remotely. If you are reading this, you are qualified to attend. Congratulations! Read on for more information.

Continue reading “You are invited to MetaBrainz Summit 24”

MetaBrainz Summit 2023

As always, the silliest photo is the best photo. Left to right: aerozol, zas, outsidecontext, mayhem, yvanzo, bitmap, monkey, kellnerd, akshaaatt, reosarevok, laptop: atj, lucifer

A year has flown by and once again the MetaBrainz team found itself in the MetaBrainz HQ in Barcelona, Spain, for #summit23. And once again we were munching on a mountain of international chocolates, hiking Mt Montserrat, bird-watching, groaning at terrible puns, testing out mayhem’s Bartendro cocktail robot (some of the team committing themselves too thoroughly to this testing), and of course discussing everything and anything MetaBrainz related. This year we had a longer summit, taking place over the week instead of the usual weekend, broken up into three days of presentations, followed by two days of hands-on ‘hacking’.

This means it’s time to strap in for a long post!

Continue reading “MetaBrainz Summit 2023”

Welcome Ansh Goyal to the MetaBrainz team!

I’m pleased to announce that we’ve added yet another former Summer of Code student/mentor to our team: Ansh Goyal. Welcome to our team, Ansh!

After Alastair Porter decided to not resume his position with MetaBrainz after his 6-month break, we were lucky to have Ansh ready to jump in to join our team. Thank you for your hard work over the years, Alastair!

Ansh will be focusing on Javascript programming, in particular React, on the BookBrainz and ListenBrainz projects. Both of these projects desperately need a lot of UI/UX work, and Ansh has proven himself that he’ll be able to help us tackle our backlog of UI projects that we need to accomplish in the short term.

Welcome to the team Ansh — we’re looking forward to your contributions!

We are ready for Summer of Code 2023 !

A belated congratulations to the 7 contributors that we selected to work with for this year’s Google Summer of Code program! 

The competition was fierce this year. MetaBrainz received a huge amount of high quality applications. Narrowing it down gets harder every year – what these contributors did right is getting in early, engaging with our community, presenting specific and detailed proposals, and proving excellent communication skills and the ability to integrate our feedback back into their proposals.

Continue reading “We are ready for Summer of Code 2023 !”

ListenBrainz moves to TimescaleDB

The ListenBrainz team has been working hard on moving our primary listen store from InfluxDB to TimescaleDB, and today at UTC 16:00 we’re going to make the switch.

We were asked on Twitter as to why we’re making the switch — and in the interest of giving a real world use case for switching, I’m writing this post. The reasons are numerous:

Openness: InfluxDB seems on a path that will make it less open over time. TimescaleDB and its dependence on Postgres makes us feel much safer in this regard.

Existing use: We’ve been using Postgres for about 18 years now and it has been a reliable workhorse for us. Our team thinks in terms of Postgres and InfluxDB always felt like a round peg in a square hole for us.

Data structure: InfluxDB was clearly designed to store server event info. We’re storing listen information, which has a slightly different usage pattern, but this slight difference is enough for us to hit a brick wall with far fewer users in our DB than we ever anticipated. InfluxDB is simply not flexible enough for our needs.

Query syntax and measurement names: The syntax to query InfluxDB is weird and obfuscated. We made the mistake of trying to have a measurement map to a user, but escaping measurement names correctly nearly drove one of our team members to the loonie bin.

Existing data: If you ever write bad data to a measurement in InfluxDB, there is no way to change it. I realize that this is a common Big Data usage pattern, but for us it represented significant challenges and serious restrictions to put simple features for our users into place. With TimescaleDB we can make the very occasional UPDATE or DELETE and move on.

Scalability: Even though we attempted to read as much as possible in order to design a scalable schema, we still failed and got it wrong. (I don’t even think that the docs to calculate scalability even existed when we first started using InfluxDB.) Unless you are using InfluxDB in exactly the way it was meant to be used, there are chances you’ll hit this problem as well. For us, one day insert speed dropped to a ridiculously low number per second, backing up our systems. Digging into the problem we realized that our schema design had a fatal flaw and that we would have drastically change the schema to something even less intuitive in order to fix it. This was the event that broke the camel’s back and I started searching for alternatives.

In moving to TimescaleDB we were able to delete a ton of complicated code and embrace a DB that we know and love. We know how Postgres scales, we know how to put it into production and we know its caveats. TimescaleDB allows us to be flexible with the data and the amazing queries that can be performed on the data is pure Postgres love. TimescaleDB still requires some careful thinking over using Postgres, it is far less than what is required when using InfluxDB. TimescaleDB also gives us a clear scaling path forward, even when TimescaleDB is still working on their own scaling roadmap. If TimescaleDB evolves anything like Postgres has, I can’t wait to see this evolution.

Big big thanks to the Postgres and TimescaleDB teams!

State of the Brainz: 2019 MetaBrainz Summit highlights

The 2019 MetaBrainz Summit took place on 27th–29th of September 2019 in Barcelona, Spain at the MetaBrainz HQ. The Summit is a chance for MetaBrainz staff and the community to gather and plan ahead for the next year. This report is a recap of what was discussed and what lies ahead for the community.

Continue reading “State of the Brainz: 2019 MetaBrainz Summit highlights”