# Integrate music streaming from Funkwhale & Navidrome (Subsonic)

**URL:** https://community.metabrainz.org/t/integrate-music-streaming-from-funkwhale-navidrome-subsonic/751757
**Category:** GSoC applications
**Tags:** listenbrainz
**Created:** [March 28, 2025, 12:27am UTC](https://community.metabrainz.org/t/integrate-music-streaming-from-funkwhale-navidrome-subsonic/751757 "2025-03-28T00:27:31Z")
**Posts on this page:** 7
**Page:** 1

<div class="post-metadata">

### Author: ![m.amanullah7](https://community.metabrainz.org/user_avatar/community.metabrainz.org/m.amanullah7/32/103905_2.png) [@m.amanullah7](https://community.metabrainz.org/u/m.amanullah7)
#### Post date: [March 28, 2025, 12:27am UTC](https://community.metabrainz.org/t/integrate-music-streaming-from-funkwhale-navidrome-subsonic/751757/1 "2025-03-28T00:27:31Z")

</div>

**Name:** Mohammad Amanullah  
**Email:** [ullahaman072003@gmail.com](mailto:ullahaman072003@gmail.com)  
**Matrix Handle:** @m.amanullah7:matrix.org  
**IRC Nickname:** @m.amanullah7  
**LinkedIn:** [https://www.linkedin.com/in/mananullah7/](https://www.linkedin.com/in/mananullah7/)  
**LeetCode:** [https://leetcode.com/u/m\_amanullah7/](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](https://wecan-six.vercel.app/) 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](https://drive.google.com/file/d/1W84RZxZJ6r9gVhVOuqpO8DRAo6oTAUj1/view?usp=sharing)**

* * *

#### 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](https://github.com/metabrainz/listenbrainz-server/pulls?q=is%3Apr+author%3AmAmanullah7+is%3Aclosed)

[**PR #3233** :](https://github.com/metabrainz/listenbrainz-server/pull/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 handle `Funkwhale API` calls and `funkwhale _servers` table which will save server details as well as `OAuth2 tokens`.

- 
### Frontend:

  - Will create a `React` and `TypeScript` form in the [Connect Services](https://listenbrainz.org/settings/music-services/details/) section for users to input their `Funkwhale server url`.

- 
### 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`.

* * *

### Here is the detailed Flow Chart: [Refer Here](https://app.eraser.io/workspace/vcZrCh24qjXIRsTeG804)

 ![diagram-export-02-04-2025-23_28_28](https://community.metabrainz.org/uploads/default/original/3X/3/3/338073826df961457db71f37877f78f67f0a0d2d.jpeg)

##### Implementation

**Db Schema:** `funkwhale _servers` table to store each user `funkwhale server url` and  
`OAuth2` credentials.

```auto
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`

```auto
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`

```python
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`

```python
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](https://listenbrainz.org/settings/music-services/details/) 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

 ![Funkwhale Mockup](https://community.metabrainz.org/uploads/default/original/3X/9/6/963f0a005586d2a7315c246c3253f99dd6798a80.png)

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](https://listenbrainz.org/settings/music-services/details/) page, which will be user-friendly. Users can connect directly from the [Connect Services](https://listenbrainz.org/settings/music-services/details/) 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`

```auto
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

```typescript
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 a `navidrome_servers` table which will save `user credentials`.

- **Frontend:** Will create a React form in the [Connect Services](https://listenbrainz.org/settings/music-services/details/) 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](https://app.eraser.io/workspace/A9FS9PXHXq3AbwmcPop3):

 ![Navidrome](https://community.metabrainz.org/uploads/default/original/3X/c/f/cf968675d7cfc263a87ccc408d52384279f2ce27.jpeg)

* * *

**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

 ![NavidromeSucess](https://community.metabrainz.org/uploads/default/original/3X/8/5/854ea669dcf39b8fcaeda6466806d3715ae94e7c.png)  
 ![NavidromeDisable](https://community.metabrainz.org/uploads/default/original/3X/b/a/bae2116b6039fbf73d5915d4c53b643b8db254f5.png)

## Frontend

Will create a React form in the[Connect Services](https://listenbrainz.org/settings/music-services/details/) 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**](https://docs.google.com/document/d/1GeEOd41sst6cH2_A0q02BGU8hwB0tJrbgeeH_CrZ2W0/edit?usp=sharing)

* * *

## 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.).

- 
### Week 2-3: Backend

  - Will add `funkwhale_servers` table to store server URLs and OAuth2 tokens.
  - Will add `FunkwhaleService` class in `listenbrainz/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.

- 
### 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.

- 
### Week 6-7: FunkwhalePlayer

  - Will add `FunkwhalePlayer` class in `BrainzPlayer.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.

* * *

## 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 in `listenbrainz/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 in `BrainzPlayer.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](https://musicbrainz.org/doc/MusicBrainz_Identifier)) 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](https://github.com/mAmanullah7/HouseHelp-Hub)  
 [HousehelpHub Link](https://amanullah7.pythonanywhere.com/)**

**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](https://github.com/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.

---

<div class="post-metadata">

### Author: ![julian45](https://community.metabrainz.org/user_avatar/community.metabrainz.org/julian45/32/23163_2.png) [@julian45](https://community.metabrainz.org/u/julian45)
#### Post date: [March 28, 2025, 4:50pm UTC](https://community.metabrainz.org/t/integrate-music-streaming-from-funkwhale-navidrome-subsonic/751757/2 "2025-03-28T16:50:49Z")

</div>

Hi,

GSoC proposals should be under the [GSoC applications](https://community.metabrainz.org/c/metabrainz/gsoc-applications/9) community; please move this post there if you would like it to be considered for GSoC.

---

<div class="post-metadata">

### Author: ![m.amanullah7](https://community.metabrainz.org/user_avatar/community.metabrainz.org/m.amanullah7/32/103905_2.png) [@m.amanullah7](https://community.metabrainz.org/u/m.amanullah7)
#### Post date: [March 29, 2025, 5:39am UTC](https://community.metabrainz.org/t/integrate-music-streaming-from-funkwhale-navidrome-subsonic/751757/3 "2025-03-29T05:39:48Z")

</div>

Thanks @julian45 for pointing out it’s updated now!

---

<div class="post-metadata">

### Author: ![m.amanullah7](https://community.metabrainz.org/user_avatar/community.metabrainz.org/m.amanullah7/32/103905_2.png) [@m.amanullah7](https://community.metabrainz.org/u/m.amanullah7)
#### Post date: [March 29, 2025, 5:44am UTC](https://community.metabrainz.org/t/integrate-music-streaming-from-funkwhale-navidrome-subsonic/751757/4 "2025-03-29T05:44:59Z")

</div>

Thanks @chaban for moving my proposal to Gsoc Section really appreciate it!

---

<div class="post-metadata">

### Author: ![lucifer](https://community.metabrainz.org/letter_avatar_proxy/v4/letter/l/ad7895/32.png) [@lucifer](https://community.metabrainz.org/u/lucifer)
#### Post date: [April 1, 2025, 10:12am UTC](https://community.metabrainz.org/t/integrate-music-streaming-from-funkwhale-navidrome-subsonic/751757/5 "2025-04-01T10:12:38Z")

</div>

Hi!

Thanks for the proposal.

We discussed this internally and I think you should focus on just integrating funkwhale, a subsonic implementation. But it supports OAuth instead of subsonic’s token + salt/password implementation as it is much more secure. Further, as you intend to do a 175 hours project. I think focussing on integration of just one service is the right scope.

You can look into existing spotify/apple music/soundcloud code to see how to integrate services using OAuth2. It should be somewhat similar except that anyone can run their own funkwhale server so you will still need the form you have mentioned.

Here are some pointers to help you understand and re-draft the proposal:

1. A form to let the user specify the Funkwhale server’s URL
2. The backend uses the submitted url to create a Funkwhale app (if one already doesn’t exist for that server), using Funkwhale’s `api/v1/oauth/apps` endpoint.
3. After the app has been created, its the regular OAuth2 flow.

You can find more details about Funkwhale’s OAuth2 implementation at [Swagger UI](https://docs.funkwhale.audio/swagger/) and [API authentication — funkwhale 1.4.0 documentation](https://docs.funkwhale.audio/developer/api/authentication.html#create-an-application).

---

<div class="post-metadata">

### Author: ![m.amanullah7](https://community.metabrainz.org/user_avatar/community.metabrainz.org/m.amanullah7/32/103905_2.png) [@m.amanullah7](https://community.metabrainz.org/u/m.amanullah7)
#### Post date: [April 1, 2025, 7:53pm UTC](https://community.metabrainz.org/t/integrate-music-streaming-from-funkwhale-navidrome-subsonic/751757/6 "2025-04-01T19:53:52Z")

</div>

Thanks for the review @lucifer!!  
Surely i will updated the proposal as per your review.

Should i also create a new mockup present mockup is enough just i’ll remove the username password form and also i need to modify my flowchart?

I have 80 days summer vacation starting from may to august! i can dedicate time, what if i wants to do this project 350hrs? what additional features i need to integrate/implement!?

---

<div class="post-metadata">

### Author: ![m.amanullah7](https://community.metabrainz.org/user_avatar/community.metabrainz.org/m.amanullah7/32/103905_2.png) [@m.amanullah7](https://community.metabrainz.org/u/m.amanullah7)
#### Post date: [April 4, 2025, 10:13am UTC](https://community.metabrainz.org/t/integrate-music-streaming-from-funkwhale-navidrome-subsonic/751757/7 "2025-04-04T10:13:37Z")

</div>

@lucifer I’ve updated my proposal as per review, if there any improvements do let me know!
