Name: Mohammad Amanullah
Email: ullahaman072003@gmail.com
Matrix Handle: @m.amanullah7:matrix.org
IRC Nickname: @m.amanullah7
LinkedIn: https://www.linkedin.com/in/mananullah7/
LeetCode: https://leetcode.com/u/m_amanullah7/
Mentor: Lucifer/mayhem
Timezone: Indian Standard Time (GMT +5:30)
Languages: English, Hindi
I’m Mohammad Amanullah, currently a pre final year student at National Institute of Technology Agartala and along with that i’m a diploma level student at Indian Institute of Technology Madras. My coding journey started in jan 2023 just few month after joining college, my journey started with c college course , then i decided to shift c++ as i was interested
in competitive programming(cp), and slowly i started enjoying doing cp, in second year python course introduced and i loved python other than any other language, and after that a new course and project introduced “MAD1” (modern application development) in which my techstack was Flask, jinja2, bootstrap,js this how my development journey started and in this course project i scored 100/100 S grade.
Other than my academic i’m leading WeCan a social club at NIT Agartala, student led non profit organisation working toward the empowerment of underprivileged children through education
When i’m neither coding and nor in weCan then i must be playing Cricket, Football or listening songs on loudspeaker, sometimes i also play BGMI and i’m not big fan of movies until unless its science fiction
CV: AmanullahxCv.pdf
Why Me?
The strongest thing about me is my problem solving skills as a competitive programmer. I get intuition very fast and even if I don’t have knowledge or its the first time I’m seeing it then I learn very fast as my never give up attitude never lets me give up.
In the past few months i’m familiarized with listenbrainz codebase, and trying to contribute as much as i can. In this period i’ve interacted with mentors in community and they helped me to throughout the time and i learned a lot.
My Pull Request
PR #3233: 404 Error Fix in explore.py
: Improved error handling for the “Music Neighborhood” page.
The root cause was that the fetchone()
method returned None when the database query didn’t find any matching records, likely because the artist name or ID I was searching for didn’t exist in the database
Proposed Project
Project length: 350 hours
Allow users to play music from their Funkwhale
servers as well as Navidrome
directly in BrainzPlayer
, both are self hoisted music streaming platform. 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
.
NOTE: if OAuth2
available then will implement it instead of basic authentication (username/password + salt)
Funkwhale (175 hours):
-
Backend:
Funkwhaleservice
class that will handleFunkwhale API
calls andfunkwhale _servers
table which will save server details as well asOAuth2 tokens
.
-
Frontend:
- Will create a
React
andTypeScript
form in the Connect Services section for users to input theirFunkwhale server url
.
- Will create a
-
BrainzPlayer Integration:
- For this I need to create a
FunkwhalePlayer
class toBrainzPlayer
so users cansearch
andstream
tracks
, and we also need to take care of properstate management
.
- For this I need to create a
Here is the detailed Flow Chart: Refer Here
Implementation
Db Schema: funkwhale _servers
table to store each user funkwhale server url
and
OAuth2
credentials.
CREATE TABLE funkwhale_servers (
user_id INTEGER NOT NULL,
host_url TEXT NOT NULL,
client_id TEXT,
client_secret TEXT,
access_token TEXT,
refresh_token TEXT,
token_expiry INTEGER,
PRIMARY KEY (user_id, host_url)
);
Will add this table via a migration and I need to create a function in db/funkwhale.py
which will save and retrieve server details. I’ll also add validation to check for duplicate
entries and ensure the host_url
is a valid URL
format before saving.
I’ll define a funkwhaleServer
model in listenbrainz/db/model/funkwhale.py
to map the table schema:
listenbrainz/db/models/funkwhale.py
from sqlalchemy import Column, Integer, Text, PrimaryKeyConstraint
from listenbrainz.db import Base
class FunkwhaleServer(Base):
__tablename__ = 'funkwhale_servers'
user_id = Column(Integer, nullable=False)
host_url = Column(Text, nullable=False)
client_id = Column(Text)
client_secret = Column(Text)
access_token = Column(Text)
refresh_token = Column(Text)
token_expiry = Column(Integer)
__table_args__ = (
PrimaryKeyConstraint('user_id', 'host_url', name='pk_funkwhale_servers'),
)
Now will create funkwhaleService
class which will handle both API
calls as well as OAuth2
authentication
Class added: listenbrainz/domain/funkwhale_services.py
import requests
import time
from listenbrainz.db import funkwhale as db_funkwhale
class FunkwhaleService:
def __init__(self, user_id: int):
self.user_id = user_id
self.servers = db_funkwhale.get_funkwhale_servers(user_id)
if not self.servers:
raise ValueError("No Funkwhale servers configured for user")
self.server = self.servers[0]
self.base_url = self.server.host_url
self.client_id = self.server.client_id
self.client_secret = self.server.client_secret
self.access_token = self.server.access_token
self.refresh_token = self.server.refresh_token
self.token_expiry = self.server.token_expiry
def create_funkwhale_app(self) -> tuple[str, str]:
url = f"{self.base_url}/api/v1/oauth/apps"
data = {
"client_name": "ListenBrainz",
"scopes": "read:library",
"redirect_uris": "https://listenbrainz.org/callback/funkwhale"
}
response = requests.post(url, json=data, timeout=5)
response.raise_for_status()
app_data = response.json()
return app_data["client_id"], app_data["client_secret"]
def get_authorization_url(self) -> str:
"""
Generate OAuth2 authorization URL.
"""
auth_reqst = {
"client_id": self.client_id,
"response_type": "code",
"redirect_uri": "https://listenbrainz.org/callback/funkwhale",
"scope": "read:library"
}
return f"{self.base_url}/oauth/authorize?{urlencode(auth_reqst)}"
def exchange_code_for_token(self, code: str) -> dict:
"""
Exchange of authorization code to access/refresh tokens.
"""
url = f"{self.base_url}/api/v1/oauth/token"
data = {
"grant_type": "authorization_code",
"code": code,
"client_id": self.client_id,
"client_secret": self.client_secret,
"redirect_uri": "https://listenbrainz.org/callback/funkwhale"
}
response = requests.post(url, data=data, timeout=5)
return response.json()
def search(self, title: str, artist: str) -> list:
"""
Search for tracks using Funkwhale API.
"""
if self.token_expiry and self.token_expiry <= int(time.time()):
token_data = self.refresh_access_token()
self.access_token = token_data["access_token"]
self.refresh_token = token_data.get("refresh_token", self.refresh_token)
self.token_expiry = int(time.time()) + token_data["expires_in"]
db_funkwhale.save_funkwhale_server(self.user_id, self.base_url, self.client_id, self.client_secret,
self.access_token, self.refresh_token, self.token_expiry)
url = f"{self.base_url}/api/v1/tracks"
headers = {"Authorization": f"Bearer {self.access_token}"}
params = {"q": f"{title} {artist}"}
response = requests.get(url, headers=headers, params=params, timeout=5)
return response.json().get("results", [])
I’ll also add an API endpoint
in webserver/views/api.py
to handle funkwhale server
configuration:
listenbrainz/webserver/views/api.py
from flask import Blueprint, request, jsonify, redirect
from listenbrainz.domain.funkwhale_services import FunkwhaleService
from listenbrainz.db import funkwhale as db_funkwhale
api_bp = Blueprint('api', __name__)
@api_bp.route('/funkwhale-config', methods=['POST'])
def funkwhale_config():
data = request.get_json()
user_id = data.get('user_id')
host_url = data.get('host_url')
if not all([user_id, host_url]):
return jsonify({'error': 'Missing required fields'}), 400
servers = db_funkwhale.get_funkwhale_servers(user_id)
server = next((s for s in servers if s.host_url == host_url), None)
if not server:
db_funkwhale.save_funkwhale_server(user_id, host_url)
service = FunkwhaleService(user_id)
client_id, client_secret = service.create_funkwhale_app()
db_funkwhale.save_funkwhale_server(user_id, host_url, client_id, client_secret)
else:
service = FunkwhaleService(user_id)
auth_url = service.get_authorization_url()
return jsonify({'redirect_url': auth_url}), 200
@api_bp.route('/callback/funkwhale', methods=['GET'])
def funkwhale_callback():
"""
Handled the Funkwhale OAuth2 callback.
"""
code = request.args.get('code')
user_id = request.args.get('user_id')
if not code or not user_id:
return jsonify({'error': 'Missing code or user_id'}), 400
service = FunkwhaleService(int(user_id))
token_data = service.exchange_code_for_token(code)
db_funkwhale.save_funkwhale_server(user_id, service.base_url, service.client_id, service.client_secret,
token_data["access_token"], token_data["refresh_token"],
int(time.time()) + token_data["expires_in"])
return redirect("https://listenbrainz.org/settings/music-services/details/?success=funkwhale")
Frontend: Will create a React form in the Connect Services section for users to input their funkwhale host_url
details. The form will include fields for host URL a Connect to flunkwhale
button to connect, and a Disable
button to disconnect. After submission, it will display a success or error message. As discussed with @lucifer I’ll embed this form directly into MusicServices.tsx
as a new section, matching the styling of existing sections (e.g., SoundCloud, Apple Music
) with a white background, orange underline for the heading, and green/gray buttons.
Mockup
This is how we will send a POST
request to the backend to initiate the Funkwhale OAuth2
flow. It will be similar to Apple Music
, Spotify
, and SoundCloud
. As discussed with @lucifer, Funkwhale supports OAuth2, so we are directly embedding a form to send a POST request from the Connect Services page, which will be user-friendly. Users can connect directly from the Connect Services page instead of navigating to a new page.
Note: This is a basic mockup and can be changed according to reviews. It will follow the same styling as Spotify, SoundCloud, etc.
Class added in `MusicServices.tsx:
frontend/js/src/settings/music-service/details/MusicServices.tsx
import React, { useState } from 'react';
import './MusicServices.css';
const FunkwhaleSettings: React.FC = () => {
const [hostUrl, setHostUrl] = useState<string>('');
const [message, setMessage] = useState<string>('');
const [isConnected, setIsConnected] = useState<boolean>(false);
const userId = 1; // Placeholder; will fetch from user context
const handleConnect = async () => {
try {
const response = await fetch('/api/funkwhale-config', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
user_id: userId,
host_url: hostUrl,
}),
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || 'Failed to connect');
}
window.location.href = data.redirect_url; // Redirect to Funkwhale OAuth2 authorization
} catch (error) {
setMessage(error.message);
setIsConnected(false);
}
};
const handleDisable = () => {
setIsConnected(false)
setMessage('Funkwhale connection disabled')
// Logic for the removal of server details from the database (will be implemented)
};
return (
<div className="service-section">
<h2>Funkwhale</h2>
<p>Connect to your Funkwhale server to play music on ListenBrainz.</p>
<div className="form-group">
<label htmlFor="host-url">Host URL</label>
<input
type="text"
id="host-url"
value={hostUrl}
onChange={(e) => setHostUrl(e.target.value)}
placeholder="e.g., https://my-funkwhale.server"
/>
</div>
<div className="button-group">
<button className="connect-btn" onClick={handleConnect} disabled={isConnected}>
<span className="checkmark">{isConnected ? '✔' : ''}</span>
Play music on ListenBrainz
</button>
<button className="disable-btn" onClick={handleDisable} disabled={!isConnected}>
Disable
</button>
<p className="button-message">
{isConnected
? 'Connect to your Funkwhale server to play music using Funkwhale on ListenBrainz.'
: 'You will not be able to listen to music on ListenBrainz using Funkwhale.'}
</p>
</div>
{message && <p className={isConnected ? 'success-message' : 'error-message'}>{message}</p>}
</div>
);
};
// To include FunkwhaleSettings in MusicServices.tsx
const MusicServices: React.FC = () => {
return (
<div className="music-services">
<FunkwhaleSettings />
</div>
);
};
export default MusicServices;
BrainzPlayer Integration: For this I need to create a funkwhalePlayer
class to BrainzPlayer
so users can search
and stream
tracks
, and we also need to take care of proper state management
. I’ll add the funkwhalePlayer
class to the existing BrainzPlayer.tsx
without creating a new page.
Class added in BrainzPlayer.tsx
:
frontend/js/src/brainzplayer/BrainzPlayer.tsx
import React from 'react';
import { FunkwhaleService } from '../../domain/funkwhale_services'
interface Track {
id: string;
title: string;
artist: string;
duration: number;
streamUrl: string;
}
interface BrainzPlayerState {
currentTrack: Track | null;
seekPosition: number;
progress: number;
isPlaying: boolean;
}
class FunkwhalePlayer {
private funkwhaleService: FunkwhaleService;
constructor(funkwhaleService: FunkwhaleService) {
this.funkwhaleService = funkwhaleService;
}
async searchTrack(title: string, artist: string): Promise<Track[]> {
const tracks = await this.funkwhaleService.search(title, artist);
return tracks.map((track: any) => ({
id: track.id,
title: track.title,
artist: track.artist?.name || "Unknown Artist",
duration: track.duration || 0,
streamUrl: ''
}));
}
// Pseudocode for remaining methods
async streamTrack(trackId: string): Promise<string> {
"""
Get stream URL for a track using Funkwhale API.
"""
streamUrl = await self.funkwhaleService.stream(trackId)
return streamUrl
}
}
class BrainzPlayer extends React.Component<{}, BrainzPlayerState> {
private funkwhalePlayer: FunkwhalePlayer;
private audioElement: HTMLAudioElement | null = null;
constructor(props: {}) {
super(props);
this.state = {
currentTrack: null,
seekPosition: 0,
progress: 0,
isPlaying: false
};
const funkwhaleService = new FunkwhaleService(1); // Placeholder user_id
this.funkwhalePlayer = new FunkwhalePlayer(funkwhaleService);
}
async playTrack(title: string, artist: string) {
const tracks = await this.funkwhalePlayer.searchTrack(title, artist);
if (tracks.length === 0) {
console.error('Track not found');
return;
}
const track = tracks[0];
const streamUrl = await this.funkwhalePlayer.streamTrack(track.id);
track.streamUrl = streamUrl;
this.setState({
currentTrack: track,
seekPosition: 0,
progress: 0,
isPlaying: true
});
if (this.audioElement) {
this.audioElement.src = streamUrl;
this.audioElement.play();
}
}
handleTimeUpdate = () => {
"""
Update the playback progress and submit the listens after 40-50% of its duration.
"""
if self.audioElement and self.state.currentTrack:
currentTime = self.audioElement.currentTime
duration = self.state.currentTrack.duration
progress = (currentTime / duration) * 100
self.setState({
seekPosition: currentTime,
progress: progress
})
if currentTime >= 200 or progress >= 40:
self.submitListen()
};
submitListen = async () => {
"""
Submit listen to /1/submit-listens endpoint.
"""
// Logic to submit the listen (to be implemented)
};
render() {
return (
<div className="brainz-player">
<audio
ref={(el) => (this.audioElement = el)}
onTimeUpdate={this.handleTimeUpdate}
/>
{/* UI elements to track the info, progress bar, play/pause buttons */}
</div>
);
}
}
export default BrainzPlayer;
Navidrome (175 hours):
Allow users to play music from their Navidrome servers
directly in BrainzPlayer
. There are mainly 3 core functionalities which I need to solve:
-
Backend:
NavidromeService
class that will handle Navidrome API calls and anavidrome_servers
table which will saveuser credentials
. -
Frontend: Will create a React form in the Connect Services section for users to input their Navidrome server details.
-
BrainzPlayer Integration: For this I need to create a
NavidromePlayer
class to BrainzPlayer so users can search and stream tracks, and we also need to take care of proper state management.
Here is the detailed Flow Chart: Refer Here:
Db Schema: navidrome_servers
table to store Navidrome credentials, which should support multiple servers per user.
Will add this table via a migration and I need to create a function in db/navidrome.py
which will save and retrieve server details. I’ll also add validation to check for duplicate entries and ensure the host_url
is a valid URL format before saving.
I’ll define a NavidromeServer
model in listenbrainz/db/model/navidrome.py
to map the table schema
I’ll also add an API endpoint
in webserver/views/api.py
to handle Navidrome server
configuration:
Mockup
Frontend
Will create a React form in the Connect Services section for users to input their Navidrome server details. The form will include fields for host URL, username, and password
, a Play music on ListenBrainz button to connect
, and a Disable button to disconnect
. After submission, it will display a success or error message. As discussed with @lucifer I’ll embed this form directly into MusicServices.tsx
as a new section, matching the styling of existing sections (e.g, SoundCloud, Apple Music) with a white background, orange underline for the heading, and green/gray buttons.
This is how we will directly send a POST request to the backend to validate the Navidrome server
. It will be similar as Apple Music, Spotify, Sound Cloud
. As discussed with @lucifer Navidrome doesn’t support OAuth2
if it’s by the timeline will implement the OAuth2
else we will continuing with basic authentication. we are directly embedding a form to send a post request directly from connected service page which will be user friendly to use them and they can directly from connect service page instead of navigating to a new page.
Note: Max Word limit exceeded for Navindrome Integration you can check my proposal pdf:
Integrate music streaming from Funkwhale & Navidrome
Timeline (12 Weeks, 350 Hours) - Funkwhale and Navidrome
Phase 1: Funkwhale
-
Week 1: Community Bonding, Deep research, local env setup.
- Go throughout the
Funkwhale API
, explore and research. - For testing set up a local
Funkwhale server
. - Decide and finalise the implementation and finalise research on
Funkwhale API
and
OAuth2 flow
. - Start the initial project setup (e.g,
environment configuration
,dependencies
, etc.).
- Go throughout the
-
Week 2-3: Backend
- Will add
funkwhale_servers
table to store server URLs and OAuth2 tokens. - Will add
FunkwhaleService
class inlistenbrainz/domain/funkwhale_services.py
which will
handle API calls and also the OAuth2 authentication. - Will add
/api/funkwhale-config
endpoint which will initiate the OAuth2 flow. - Cross check and validate API endpoint functionality with the local Funkwhale server.
- Will add
-
Week 4-5: Frontend
- Refine the mockup and create frontend same as mockup components using React and
TypeScript. - Will add the Funkwhale UI into
MusicServices.tsx
(form for server URL, connect/disconnect
buttons). - Integrate the backend with frontend through
/api/funkwhale-config
which will initiate
OAuth2 flow. - Testing of the OAuth2 redirect and callback flow.
- Refine the mockup and create frontend same as mockup components using React and
-
Week 6-7: FunkwhalePlayer
- Will add
FunkwhalePlayer
class inBrainzPlayer.tsx
. - Will add search and streaming features using Funkwhale API.
- Validation of components to check state effectively (e.g., track progress, play/pause).
- Verify that the listens are submitted accurately and correctly to
/1/submit-listens
after 40- 50% of track duration.
- Will add
Phase 2: Navidrome
-
Week 8-9: Buffer & Deep Research & Local Env Setup
- Fix Bugs, improvement, resolve issues (Funkwhale).
- Deep research, collect more information about the Navidrome and Subsonic API.
- Navidrome local environment setup for testing.
- Decide the research on Subsonic API and authentication.
- Check for Navidrome’s latest OAuth2 authentication available or not.
- Use if OAuth2 available for Navidrome’s authentication else use basic authentication
(username/password + salt).
-
Week 10: Backend
- Fix Bugs, improvement, resolve issues (Funkwhale).
- Will add
navidrome_servers
table which will store server URLs, usernames, and passwords. - Will integrate the
NavidromeService
class inlistenbrainz/domain/navidrome_services.py
which will handle Subsonic API calls also handle authentication. - Will add
/api/navidrome-config
endpoint which will validate server details. - Using a local Navidrome server validate the API endpoint functionality.
-
Week 11: Frontend
- Fix Bugs, improvement, resolve issues (Funkwhale).
- Refine mockup and create frontend same as mockup components using React and
TypeScript. - Will add the Navidrome UI into
MusicServices.tsx
(form for server URL, username,
password, connect/disconnect buttons). - Integrate the backend with frontend through
/api/navidrome-config
to validate server details. - Will add
NavidromePlayer
class inBrainzPlayer.tsx
for searching and streaming. - Validation of components to check state effectively (e.g., track progress, play/pause).
-
Week 12: Testing & Final Adjustments
- Fix Bugs, improvement, resolve issues both Funkwhale and Navidrome.
- Documentation for both Funkwhale and Navidrome.
- Review code coverage for both Funkwhale and Navidrome.
- Address feedback for both Funkwhale and Navidrome.
Community Affinities
What type of music do you listen to? (please list a series of [MBIDs](MusicBrainz Identifier - MusicBrainz) as examples)
I mostly love to listen song like pop, rock and sad and love Songs.
- Ek Din Teri Raahon ( MBID: 2dd0fb05-b7ca-4301-8d1a-ebdc9ca2673a
- Excuses (MBID: 5c1dcd12-605c-4717-bc19-095162fba01e
- Animals (MBID: 25704ed9-c86f-4f3d-991a-d293830697a0)
What aspects of MusicBrainz/ListenBrainz/BookBrainz/Picard interest you the most?
I love how ListenBrainz helps me track all my listening at one place and provide a platform to stream music from all over the application at one place. It’s open source which I also like the most .
Have you ever used MusicBrainz Picard to tag your files or used any of our projects in the past?
No as of now i didn’t use
Programming Precedents
When did you first start programming?
My coding journey started in jan 2023 just few month after joining college, my journey started with c college course , then i decided to shift c++ as i was interested in competitive programming(cp)
Have you contributed to other open source projects? If so, which projects and can we see some of your code?
No listenBrainz is my first open source in which i have contributed
If you have not contributed to open source projects, do you have other code we can look at?
I’ve created various project but most and also working on some project my recent project
In which i got perfect 100/100 s grade
HousehelpHub Github
HousehelpHub Link
What sorts of programming projects have you done on your own time?
I’ve done various different types of projects over different periods of time like HousehelpHub
, Fitometer
, sehatGPT
, EventEase
, Rback
,etc these projects gives me edge of working on different languages and can learn new tech very fast
For more detail in you can refer my github: mAmanullah7
Practical Requirements
I currently use a M1 Macbook Air, 8GB RAM, 512GB SSD
.
How much time are you available weekly, and how would you plan to use it?
In summer break, I can easily work 40 hours
a week
for the project as I won’t have any other commitments.