| Doc Version | PDF Version |
- Name: Om Mukherjee
- Matrix / IRC: meekhumor
- Email: om17092004@gmail.com
- GitHub: meekhumor
- Linkedin: om-mukherjee7
- Leetcode: meek_humor
- Timezone: UTC+05:30
- Resume: om-mukherjee-resume.pdf
Personal Introduction
Hey, I’m Om Mukherjee, a pre final year student from VJTI, Mumbai. I have been coding since my first year of college, and over time it has grown from just a skill into something i genuinely enjoy doing. In college, I’m part of the open source club ProjectX and the AI/ML club brAIn, both of these clubs have helped me grow as a developer. Over the past three years, I have built project across domains of web technologies and AI/ML. One of my key projects is Virtual Interviewer built using React.js and Django. Some of my other notable projects are Lip Reading System and AI Based Traffic Signal Control System
I participated in Hactoberfest 2024, which marked my first step in open source. Since then, I’ve contributed to organizations like JdeRobot, and MetaBrainz is the second major organization where I’ve been actively contributing. I’ve been contributing to listenbrainz since December 2025, and it has been a great learning experience working with a real world codebase and community.
Why me?
Listenbrainz uses react for frontend and python for the backend, both of which I am very comfortable with. I have worked with this stack in multiple projects, so I understand how the frontend and backend interact, how api are structured. And beyond technical familiarity, I have contributed to listenbrainz, where I worked across multiple parts of the system including apis, frontend components, spark, troi, mbid_mapper and the webserver. I have also set up musicbrainz, listenbrainz, troi, and spark locally with sample data, which gave me a deeper understanding of how the entire pipeline is working together.
Here are some of my contributions:
Project Overview
This project aims to improve playlist management and organization in listenbrainz by introducing better ways for users to search, sort, and organize their playlists. Currently, playlist functionality is limited, making it difficult for users to efficiently manage their playlists.
The project will focus on enhancing the overall user experience by implementing playlist search, improved organization methods, flexible track sorting options, and integration with musicbrainz collections.
Implementation Plan
Feature 1: Per User Playlist Search
At the moment, users can't search their own playlists from the /user/<username>/playlist page. The existing global /search/ page searches all public playlists on LB, but it does not scope results to a specific user.
There is already an endpoint search_user_playlist present in /webserver/views/api.py but it is frequently getting timeout due to some bugs which i will fix:
1. Fixing the postgres trigram performance issue
The current SQL condition includes:
SELECT similarity(pl.name, :query) AS name_similarity ...
WHERE name_similarity > 0.1
So right now it does the full sequential scan of every playlist in the database. search_playlists_for_user() also contains EXISTS subqueries to check for playlist_collaborator, etc permissions which forces postgres to evaluate those subqueries on every single playlist in the database before filtering, thus triggering timeout.
For this, I will create the trigram indexes on the playlist table:
CREATE INDEX playlist_name_trgm_idx
ON playlist.playlist
USING GIN (name gin_trgm_ops);
CREATE INDEX playlist_description_trgm_idx
ON playlist.playlist
USING GIN (description gin_trgm_ops);
Also, I will rewrite the query str to use these trigram indexes using the % operator. I will also add an ILIKE fallback for exact substring matches incase the trigram threshold is not achieved.
WHERE (pl.name % :query OR pl.description % :query)
OR (pl.name ILIKE :query OR pl.description ILIKE :query)
2. Implementing Proper Playlist Type Scoping
Since there are two tabs: Playlists and Collaborations and the frontend sends a ?type=collaborative parameter which the backend currently ignores. I will update the database method to construct the filtering logic based on the playlist_type, This will ensure that the collaborative tab returns only the collaborative playlists during search
if playlist_type == "collaborations":
association_condition = "playlist_collaborator.collaborator_id = :user_id"
else:
association_condition = "(pl.creator_id = :user_id OR pl.created_for_id = :user_id)"
3. Fixing Missing Authentication in API & Frontend
Currently the frontend does not currently include the auth token in the request due to which the user/<username>/playlist/search endpoint fails to read it. As a result, the search_playlists_for_user function cannot verify whether the requesting user has permission to access private playlists. To fix this, I will update the searchPlaylistsForUser function in APIService.ts to attach the auth header in the fetch(url).
Frontend Integration: Inline Playlist Search
I will introduce an inline search input in PlaylistSearch.tsx. This search will use a 300ms debounced handler, and will call APIService.searchPlaylistForUser() directly.
Pagination & Search State Interaction
The playlists page uses url based pagination (?page), with the default 25 playlists per page which is fetched via server. Now when the search is active, results will be stored in local react state and rendered in place of the paginated list. When the search is cleared, the original paginated view will be restored at the same page, since page state is preserved in the url search params.
Search results will have their own independent pagination using local state, this will be separate from the url based pagination used for rendering the main playlist list.
Frontend Integration: Inline Playlist Search
I will introduce an inline search input in PlaylistSearch.tsx. This search will use a 300ms debounced handler, and will call APIService.searchPlaylistForUser() directly.

Flowchart
Feature 2: Playlist Tags & Folder Organization
To improve playlist organization and discoverability, I propose introducing two mechanisms: Tags and Folders. Tags will provide flexible, lightweight categorization, while the folders will offer structured grouping for users
1. Tags
Tags will allow users to label playlists with custom keywords such as running, metal, or relax, for quick filtering and better organization.
1.1. Data Model
Tags will be stored in the additional_metadata JSONB column that already exists on the playlist.playlist table. The structure will look like:
{
"algorithm_metadata": { "source_patch": "weekly-jams" },
"tags": ["running", "metal", "2024"]
}
To ensure efficient filtering and prevent full table scans, I will add a GIN index on the tags key within additional metadata
BEGIN;
CREATE INDEX playlist_tags_gin_idx
ON playlist.playlist
USING GIN ((additional_metadata->'tags'));
COMMIT;
1.2. Writing Tags
When a user saves tags from the frontend ui, the existing edit_playlist API endpoint will receive the updated tag list and call a existing database helper function (update_playlist) to insert or replace the tag key within additional_metadata using jsonb_set.
db/playlist.py
from sqlalchemy import text
import orjson
def update_playlist(db_conn, ts_conn, playlist, tags=None):
"""
Update playlist metadata (Name, description, public flag, tags)
"""
if tags is not None:
query = text("""
UPDATE playlist.playlist
SET name = :name,
description = :description,
public = :public,
additional_metadata = jsonb_set(
COALESCE(additional_metadata, '{}'::jsonb), '{tags}', :tags::jsonb
)
WHERE id = :id
""")
params = {
'id': playlist.id,
'name': playlist.name,
'description': playlist.description,
'public': playlist.public,
'tags': orjson.dumps(tags).decode("utf-8")
}
else:
query = text("""
UPDATE playlist.playlist
SET name = :name,
description = :description,
public = :public,
additional_metadata = :additional_metadata
WHERE id = :id
""")
params = {
'id': playlist.id,
'name': playlist.name,
'description': playlist.description,
'public': playlist.public,
'additional_metadata': orjson.dumps(playlist.additional_metadata or {}).decode("utf-8")
}
1.3. New API Endpoints
GET /1/playlist/user/<user_name>/tags: It will return all distinct tags the user has used, which will be used to populate the filter bar in ui.
def get_all_tags_for_user(db_conn, ts_conn, user_id: int) -> List[str]:
sql = text("""
SELECT DISTINCT jsonb_array_elements_text(additional_metadata->'tags') AS tag
FROM playlist.playlist
WHERE creator_id = :user_id
AND additional_metadata ? 'tags'
ORDER BY tag
""")
return [row[0] for row in ts_conn.execute(sql, {"user_id": user_id})]
GET /1/playlist/user/<user_name>playlists?tag=<tag>: It will return playlists filtered by a specific tag. Filtering will respect privacy rules (owners see private playlists whereas others will see only public ones).
def get_playlists_by_tag(db_conn, ts_conn, user_id, tag, include_private=False, count=25):
privacy_clause = "" if include_private else "AND pl.public = true"
sql = text("""
SELECT pl.id, pl.mbid, pl.creator_id, pl.name, pl.description,
pl.public, pl.created, pl.last_updated, pl.copied_from_id,
pl.created_for_id, pl.additional_metadata,
copy.mbid AS copied_from_mbid
FROM playlist.playlist AS pl
LEFT JOIN playlist.playlist AS copy ON pl.copied_from_id = copy.id
WHERE pl.creator_id = :creator_id
{privacy_clause}
AND pl.additional_metadata->'tags' @> to_jsonb(:tag::text)
ORDER BY pl.created DESC
LIMIT :count OFFSET :offset
""")
1.4. Frontend UI
Playlist card
Tag filter bar
Tag editor:
2. Folders
Folders will offer a structured way to group playlists, especially for
users who maintain many playlists.
2.1. Data Model
Unlike tags, folders are better implemented using dedicated relational tables rather than JSONB. I propose introducing playlist.playlist_folder and playlist.playlist_folder_item.
The playlist_folder table will store the folder metadata like creator, name, timestamps. whereas,
The playlist_folder_item table will map the playlists to folders and also include a position column.
The position field will enable drag and drop reordering of playlists within a folder.
2.2. API and UI Integration
Folder endpoints will follow the same design pattern as that of tag endpoints:
- Create, update, delete folders
- Add/remove playlists from folders
- Reorder playlists within folders
On the frontend, I will introduce a sidebar on the playlists page listing folder names. Selecting a folder will filter out the playlist grid to show only playlists within that folder.
Now when a user has multiple playlists it becomes tedious to manually tag them all so I will create a system which will allow users to select dozens of playlists and move them into folders in a single gesture so that users can drag and drop the playlist to any of the folders.
Why Both Tags and Folders Are Necessary?
At first, I thought a tags only system would be enough for organizing playlists. But after going through this thread (if you get time, do read this :)) ,I realized tags rely heavily on search, and search is unreliable. You’re never fully sure if results are complete, and everything depends on tagging things correctly. Plus, tags require recall, you need to remember what tag you used (“road-trip” vs “travel”) instead of just browsing, which is much more natural. With 80+ playlists, this becomes a real problem: there’s no spatial organization, no clear structure, and no way to systematically explore your playlists. Folders, on the other hand, give a predictable, canonical structure which you can trust what you see is complete. Over time you start creating overly specific tags like “workout-morning” or “workout-gym”. And while tags offer flexibility, they make the UI harder to browse and mentally track.
Feature 3: In Playlist Track Sorting
This feature will introduce flexible track sorting within the playlists, which will mostly be on client side
3.1. Sorting Criteria
The sorting criteria will include date added, track title, artist name, shuffle and manual order
Sorting by title, artist, and date added will work entirely on the client side because the required metadata is already included in the JSPF payload when the playlist page loads so no additional API calls are required for sorting. the frontend will sort the tracks array locally in memory.
If a user decides to visually sort the playlist by artist, track, etc, the play all button will instantly play the tracks in that new sorted order and If the user refreshes the page, then the playlist reverts back to its original order.
3.2 Manual Reordering
Now if the user wants to permanently reorder the tracks of the playlist, they must select the manual sort option from the dropdown.
When dragging a track in manual mode, the frontend will simply call the existing APIService.movePlaylistItem to update track order in the backend.
3.1. Frontend UI
Drag disabled when other sorting criterion is selected
Drag enabled when manual order as sorting criterion is selected
3.2 Flowchart
Feature 4: MusicBrainz Collections as Playlists
I propose adding MB collections as a third tab alongside Playlists and Collaborative tab without requiring an import allowing users to view and manage their MB collections directly from listenbrainz
Collections will be fetched dynamically from the MB database. Users will be able to add or remove recordings from their collections directly within listenbrainz, and those changes will be written back to MB via its web service api.
4.1. Reading collections (listing + fetching tracks)
Collections will be queried directly from the MB database. No collection data will be stored in the listenbrainz database.
A new backend blueprint (mb_collection_api.py) will expose endpoints to list a user’s public MB collections and fetch the recordings from a specific collection
The listing endpoint retrieves public collections for a given user and returns metadata including collection name, entity type (recording, release, or release group), and item count.
When fetching collection tracks, the endpoint dynamically determines the collection type:
- If the collection contains recordings, tracks are fetched directly.
- If the collection contains releases or release groups, recordings are derived by joining through medium and track tables.
The response is returned in a JSPF compatible format so that it can reuse existing playlist rendering logic on the frontend. This will allow MB collections to behave visually like playlists.
listenbrainz/webserver/views/mb_collection_api.py
@mb_collection_api_bp.get("/user/<user_name>/collections")
""" List a user's public MB collections """
def get_user_mb_collections(user_name):
sql = """
SELECT ec.gid::text AS id, ec.name, ect.entity_type, COUNT(ecr.collection) AS count
FROM editor_collection ec
JOIN editor_collection_type ect ON ec.type = ect.id
LEFT JOIN editor_collection_recording ecr ON ecr.collection = ec.id
JOIN editor ed ON ec.editor = ed.id
WHERE ed.name = %s AND ec.public = TRUE
AND ect.entity_type IN ('recording', 'release', 'release_group')
GROUP BY ec.gid, ec.name, ect.entity_type
ORDER BY ec.name
"""
with _get_mb_db_cursor() as mb_cur:
mb_cur.execute(sql, (user_name,))
rows = mb_cur.fetchall()
return jsonify({"collections": [dict(r) for r in rows], "count": len(rows)})
Collection tracks endpoint:
@mb_collection_api_bp.get("/collection/<collection_id>/recordings")
def get_mb_collection_recordings(collection_id):
"""
Return recordings in an MB collection as a JSPF compatible response.
"""
limit = int(request.args.get("count", 25))
offset = int(request.args.get("offset", 0))
with _get_mb_db_cursor() as mb_cur:
mb_cur.execute("""
SELECT ec.id, ec.name, ect.entity_type
FROM editor_collection ec
JOIN editor_collection_type ect ON ec.type = ect.id
WHERE ec.gid = %s AND ec.public = TRUE
""", (collection_id,))
meta = mb_cur.fetchone()
if meta["entity_type"] == "recording":
tracks, total = _get_recording_collection_tracks(meta["id"], limit, offset)
else:
tracks, total = _get_release_collection_tracks(meta["id"], meta["entity_type"], limit, offset)
return jsonify({
"playlist": {
"title": meta["name"],
"track": [{"identifier": [f"track_prefix_{t['mbid']}"]} for t in tracks]
},
"total_count": total
})
Helper functions:
def _get_recording_collection_tracks(collection_id, limit, offset):
with _get_mb_db_cursor() as mb_cur:
mb_cur.execute("SELECT COUNT(*) FROM editor_collection_recording WHERE collection = %s", (collection_id,))
total = mb_cur.fetchone()[0]
mb_cur.execute("SELECT r.gid::text AS mbid FROM editor_collection_recording ecr JOIN recording r ON r.id = ecr.recording WHERE ecr.collection = %s ORDER BY ecr.position LIMIT %s OFFSET %s", (collection_id, limit, offset))
return [dict(r) for r in mb_cur], total
def _get_release_collection_tracks(collection_id, entity_type, limit, offset):
join_table = "editor_collection_release" if entity_type == "release" else "editor_collection_release_group"
id_col = "release" if entity_type == "release" else "release_group"
sql = f"""
SELECT rec.gid::text AS mbid FROM medium m
JOIN track t ON t.medium = m.id
JOIN recording rec ON rec.id = t.recording
JOIN {join_table} x ON x.collection = %s
JOIN release rel ON rel.id = x.{id_col} OR rel.release_group = x.{id_col}
WHERE rel.id = m.release ORDER BY m.position, t.position LIMIT %s OFFSET %s
"""
with _get_mb_db_cursor() as mb_cur:
mb_cur.execute(sql, (collection_id, limit, offset))
tracks = [dict(r) for r in mb_cur]
return tracks, len(tracks)
4.2. Writing to collections (add/remove recordings)
When a user adds or removes recordings from a collection, The frontend will call the listenbrainz endpoint which will then forward the request to the musicbrainz via web service api. The change will be immediately reflected in musicbrainz.
Musicbrainz supports bulk operations of up to approximately 400 recordings per request using endpoints such as:
PUT /ws/2/collection/<gid>/recordings/<mbid1>;<mbid2>
DELETE /ws/2/collection/<gid>/recordings/<mbid1>
listenbrainz/webserver/views/mb_collection_api.py
@mb_collection_api_bp.put("/collection/<collection_id>/recordings/<mbids>")
def add_to_mb_collection(collection_id, mbids):
"""Proxy add to MB collection api"""
requests.put(f"https://musicbrainz.org/ws/2/collection/{collection_id}/recordings/{mbids}", headers={"Authorization": f"Bearer {current_user.musicbrainz_oauth_token}"})
return jsonify({"status": "ok"})
@mb_collection_api_bp.delete("/collection/<collection_id>/recordings/<mbids>")
def remove_from_mb_collection(collection_id, mbids):
"""Proxy remove from MB collection api."""
requests.delete(f"https://musicbrainz.org/ws/2/collection/{collection_id}/recordings/{mbids}", headers={"Authorization": f"Bearer {current_user.musicbrainz_oauth_token}"})
return jsonify({"status": "ok"})
4.2. Frontend Integration:
When the MB Collections tab is active:
A new MBCollectionsList.tsx component will render collection cards displaying collection name, entity type (recording, release, release group, artist, label, area, etc) with their item count.
- Clicking a collection with entity_type (
recording, release or release group) will open a dedicated detail view which will reuse existing playlist componentsPlaylistItemCardinPlaylist.tsxfor track display. - Clicking a collection with other entity_type (
artist, label, area, etc) will open their dedicated react components (like ArtistCard or LabelList)
5. Additional Feature: Template Based Playlist Creation
I noticed that create playist in listenbrainz currently starts with an empty playlist where users have to manually add tracks after creating it.
I was thinking about adding a create from template option alongside the existing create playlist button.
Instead of starting from an empty playlist, users could choose from a few simple templates such as:
- Similar Artists
- Explore a Tag
- My Library
- My Recommended
- My Mix
This is similar to last.fm create playlist. Each of these template will ask for input (or none) and generate a playlist automatically using the existing LB Radio functionality.
When the user clicks the create from template button, a modal will open up and it will look something like this,
For each of these templates there will a LB Radio prompt as follows
| Template Name | LB Radio Prompt |
|---|---|
| My Archives | stats:user::all_time:easy |
| Fresh Finds | recs:user::unlistened:medium |
| Genre Explorer | tag:(input):easy |
| Country Beats | country:(input):easy |
| Similar Artists | artist:(input):easy |
Timeline:
Community Bonding (May 1–26) ![]()
| Week | Deliverables |
|---|---|
| 1 | Refactor search_playlists_for_user() (add include_private, playlist_type), Implement scoped search endpoint + backend unit tests. |
| 2 | Add search bar in Playlists.tsx and searchPlaylistsForUser() in APIService, Render scoped results |
| 3 | Integration tests for Feature 1, Add GIN index migration and implement get_all_tags_for_user. |
| 4 | Implement get_playlists_by_tag endpoint with privacy handling, Add tag validation inside edit_playlist. |
| 5 | Add tag editor to CreateOrEditPlaylistModal, Display tag pills on playlist cards. |
| 6 | Implement tag filter bar in Playlists.tsx, Connect filter UI to backend endpoints. |
| Mid term | Jul 8-Jun 14 |
| 7 | Implement reorder_playlist_recordings bulk update logic, Add /item/ reorder endpoint + expose recording_id in JSPF. |
| 8 | Add sort dropdown and display sorting logic in frontend, Implement a save order button wired to reorder endpoints. |
| 9 | Add unit and integration tests for reordering, Handle edge cases (shuffle, reset, large playlists). |
| 10 | Implement MB collections blueprint and add/remove endpoints, Support recording-type collections. |
| 11 | Add release/release-group collection support. |
| 12 | Add MB Collections tab and MBCollectionsList.tsx, Reuse playlist detail view for collection display. |
| 13 | Add frontend and integration test for Feature 4, Begin with backend folder (stretch goal), if on track. |
| 14 | Documentation updates and final refactoring. |
| Final term | Final submission |
Community Affinities
What type of music do you listen to?
I enjoy listening music of every kind, be it pop, ghazal, or rap. My
favorite bands are The Local Train and When Chai Met Toast.
My favourite tracks are:
- Ocean by Anuv Jain [ec26b4df-df61-44e7-9a70-3cd1f74fe06c]
- The Search by NF [15ee39d8-5d80-40d5-8779-cba9ee35e226]
- Meri Kahani by Atif Aslam [ddcec0be-285f-45e0-b2f4-ed3ecfaf8f2c]
- Sailor by Gigi Perez [4de2fca3-e37a-4399-b121-9c4a6cb298b5]
What aspects of MusicBrainz / ListenBrainz interest you the most?
I like how everything is transparent, and how i can connect to multiple music services and get my listens tracked in one place, which is quite fascinating. I also like the clean and structured codebase of listenbrainz.
Programming Precedents
When did you first start programming?
I started programming in my first year of college. What began as coursework gradually turned into genuine interest. Over time, I moved from basic problem solving to building full stack applications and AI/ML
projects.
Have you contributed to other open source projects?
Yes, before MetaBrainz I have contributed to JdeRobot. You can view my contributions here
What sorts of programming projects have you done on your own time?
- Virtual Interviewer: Web application that simulates job interview scenarios using react and django
- Traffic Signal Control System: AI model to optimize and manage urban traffic flow efficiently.
- Lip Reading System System which interprets spoken words from video using hybrid 3D CNN + LSTM model.
- Microplastics Detection System System which detects and quantifies microplastics in water samples
Practical requirements
What computer do you have available for working on your GSoC project?
I have a HP ProBook 640 G4 laptop running Ubuntu Linux. It is equipped with an Intel Core i5 processor, 16 GB RAM with 512 GB SSD
How much time do you have available per week, and how would you plan to use it?
I can work 30–40 hours per week, as I do not have other commitments during the coming summer. I plan to utilize most of my time on development and debugging.











