Personal Information
Name: Vardan Saini
School: University of Alberta
Github: https://github.com/vardansaini
Linkedin: https://www.linkedin.com/in/vardansaini/
Portfolio: Vardan Saini
IRC nickname: vscode
email: vardan1@ualberta.ca
My contributions:
Project Overview
By linking their Apple Music account with ListenBrainz, individuals are able to use ListenBrainz to play their music selections. The integration into BrainzPlayer allows for this capability. By utilizing a content resolver, we can match the MusicBrainz Identifier (MBID) to the Apple Music catalog, permitting users to transfer playlists to their Apple Music account.
Introduction
ListenBrainz currently supports integration with Spotify, but there is a growing demand for integration with other music services. Apple Music is a popular music streaming service, and integrating it into ListenBrainz will provide more options for users to access personalized recommendations. This proposal outlines the steps to integrate Apple Music into ListenBrainz.
OAuth Integration
Frontend
- The first step in integrating Apple Music into ListenBrainz is to configure OAuth on the frontend. The MusicKit
JavaScript library can be used to authenticate users with their Apple Music accounts using the OAuth protocol.<script src="https://js-cdn.music.apple.com/musickit/v3/musickit.js" data-web-components async></script>
- The developer token needs to be generated on the backend and sent to the frontend. Once the MusicKit library is
included in the web page, theconfigure
method is called to configure an instance of MusicKit on the Web.document.addEventListener('musickitloaded', async function () { try { await MusicKit.configure({ developerToken: 'DEVELOPER-TOKEN', app: { name: 'ListenBrainz', build: 'GIT-COMMIT-SHA', }, }); } catch (err) { // Handle configuration error } const music = MusicKit.getInstance(); });
- The
authorize
method can be used to authenticate the user and access their Apple Music account. This step involves
the following:music.authorize().then(musicUserToken => { submitMusicUserTokenToLB(musicUserToken); });
Backend
-
The music services callback URL (
/music-services/{service}/callback
) receives the authorization code in case of
of other music services. However, in the case of Apple Music there is no client secret oauth available. Instead we
do OAuth client side using MusicKit and once the user has granted access, the Music-User-Token is sent to server at
callback URL. -
Apple Music’s catalog varies with the user’s storefront so store we should retrieve and store it.
def get_storefront(music_user_token): headers = {'Authorization': f'Bearer {music_user_token}'} response = requests.get('https://api.music.apple.com/v1/me/storefront', headers=headers) return response.json()['data'][0]['id']
-
The token and storefront can be stored in the database to be used later.
@profile_bp.route('/music-services/apple/callback') @login_required def apple_callback(): service = AppleService() music_user_token = request.args.get('music_user_token') service.get_storefront(music_user_token) service.add_new_user(current_user.id, music_user_token, storefront) return redirect(url_for('index'))
Integrate Apple Music in BrainzPlayer
The BrainzPlayer is a web-based player used to play music on ListenBrainz from various services. It already supports playing music from Spotify, Youtube and Soundcloud.
-
MusicKit can be loaded on the page in the same as shown in the OAuth Frontend section.
-
Once the player is loaded, we can use
MusicKitInstance.{play,pause}
etc. controls and add callbacks toplaybackStateWillChange
andplaybackStateDidChange
events to integrate with BrainzPlayer controls.music.addEventListener('playbackStateWillChange', ({ oldState, state }) => { console.log(`About to change the playback state from ${oldState} to ${state}`); });
Before adding the event callbacks, we should check whether the user has authorized Apple Music.
-
Tracks can be enqueued in the MusicKit player using
setQueue
orplayNext
method and its Apple Music Identifier:await music.playNext({ song: '1561837008' });
-
If the listen does not have an Apple Music Identifier in it, we can query the search API as follows:
const results = await music.api.search(term, { types: 'songs', limit: 10, storefront: 'us' });
Then we can enqueue the desired track from the search results in the player and play it.
Build Content Resolver
The metadata content resolver is used to store metadata of the entire catalog present in an external service’s database.
Currently, ListenBrainz has such a resolver for Spotify. As a part of this project, I intend to add a similar content resolver for Apple Music as well.
-
Use the Apple Music API to get all storefronts (
https://api.music.apple.com/v1/storefronts
)[ { "id": "dz", "type": "storefronts", "href": "/v1/storefronts/dz", "attributes": { "explicitContentPolicy": "opt-in", "name": "Algeria", "defaultLanguageTag": "en-GB", "supportedLanguageTags": [ "en-GB", "fr-FR", "ar" ] } }, // more storefronts .... ]
-
Get catalog charts for each storefront to seed metadata resolver (
https://api.music.apple.com/v1/catalog/us/charts?types=songs,albums
){ "results": { "songs": [ { "chart": "most-played", "name": "Top Songs", "orderId": "most-played:songs", "next": "/v1/catalog/us/charts?chart=most-played&genre=20&limit=1&offset=1&types=songs", "data": [ { "id": "635016640", "type": "songs", "href": "/v1/catalog/us/songs/635016640", "attributes": { "albumName": "I Love You.", "composerName": "Jesse, Zachary Abels & Jeremiah Freedman", "playParams": { "id": "635016640", "kind": "song" }, "name": "Sweater Weather", "artistName": "The Neighbourhood", // ... more fields } } ], "href": "/v1/catalog/us/charts?chart=most-played&genre=20&limit=1&types=songs" } ], "albums": [ { "chart": "most-played", "name": "Top Albums", "orderId": "most-played:albums", "next": "/v1/catalog/us/charts?chart=most-played&genre=20&limit=1&offset=1&types=albums", "data": [ { "id": "1608118564", "type": "albums", "href": "/v1/catalog/us/albums/1608118564", "attributes": { "playParams": { "id": "1608118564", "kind": "album" }, "name": "mainstream sellout", "artistName": "Machine Gun Kelly", // .. more fields } } ], "href": "/v1/catalog/us/charts?chart=most-played&genre=20&limit=1&types=albums" } ] } }
Using the ids from this data, seed the content resolver.
-
Get all albums of the artist (
https://api.music.apple.com/v1/catalog/us/artists/{artist_id}/albums
)[ { "id": "1482041821", "type": "albums", "href": "/v1/catalog/us/albums/1482041821", "attributes": { "copyright": "℗ 2019 Mom+Pop", "genreNames": [ "Alternative", "Music" ], "releaseDate": "2020-02-14", "upc": "858275058666", "artwork": { "width": 3000, "height": 3000, "url": "https://is3-ssl.mzstatic.com/image/thumb/Music125/v4/0b/b2/52/0bb2524d-ecfc-1bae-9c1e-218c978d7072/Honeymoon_3K.jpg/{w}x{h}bb.jpg", "bgColor": "fffaa9", "textColor1": "030005", "textColor2": "363240", "textColor3": "353226", "textColor4": "5e5a55" }, "url": "https://music.apple.com/us/album/honeymoon/1482041821", "playParams": { "id": "1482041821", "kind": "album" }, "recordLabel": "Mom+Pop", "trackCount": 9, "name": "Honeymoon", "artistName": "Beach Bunny" } }, // .. more albums ]
-
Get all tracks of multiple albums (
https://api.music.apple.com/v1/catalog/us/albums?ids={album_ids}
){ "data": [ { "id": "1616728060", "type": "albums", "href": "/v1/catalog/us/albums/1616728060", "attributes": { "name": "Heart On My Sleeve", "contentRating": "explicit", "artistName": "Ella Mai" }, "relationships": { "tracks": { "href": "/v1/catalog/us/albums/1616728060/tracks", "data": [ { "id": "1616728064", "type": "songs", "href": "/v1/catalog/us/songs/1616728064", "attributes": { "albumName": "Heart On My Sleeve", "genreNames": [ "R&B/Soul", "Music" ], "trackNumber": 1, "durationInMillis": 197314, "releaseDate": "2022-05-06", "isrc": "USUM72203653", "artwork": { "width": 3000, "height": 3000, "url": "https://is3-ssl.mzstatic.com/image/thumb/Music112/v4/03/45/19/034519dc-9ff9-7f63-3d02-23a101a0cc3a/22UMGIM01299.rgb.jpg/{w}x{h}bb.jpg", "bgColor": "0b1b18", "textColor1": "fc977a", "textColor2": "ef5429", "textColor3": "cc7e66", "textColor4": "c14925" }, "composerName": "Charles Hinshaw Jr, Dijon McFarlane, Ella Mai Howell, NICHOLAS MATTHEW BALDING, Peter Johnson & Shah Rukh Zaman Khan", "playParams": { "id": "1616728064", "kind": "song" }, "url": "https://music.apple.com/us/album/trying/1616728060?i=1616728064", "discNumber": 1, "artistName": "Ella Mai" } }, // ... more tracks ] } } }, // ... more albums ] }
-
Store data in a normalized tables in a database schema similar to spotify content resolver and set a cache expiry.
-
Before retrieving data from API, check if it exists already in database and update cache accordingly.
Timeline
Week 1-4: Integrating Apple Music in BrainzPlayer (without OAuth), setting up controls, searching tracks and playing previews
Week 5-7: Getting Music User Token, storefront, storing it in database
Week 8-11: Build the content resolver to store Apple Music catalog metadata in ListenBrainz
Week 12-13: Buffer Period
Other Information
Tell us about the computer(s) you have available for working on your SoC project!
I have a Macbook pro 16 inch with 2.3 GHz 8 core intel i9 (9th gen)
, AMD Radeon Pro 5500M 4 GB, 16 GB 2667 MHz DDR4 and 1 TB SSD.
When did you first start programming?
Since 2011, I’ve been involved in programming using Java, and over time, I’ve had the opportunity to work on numerous projects.
What type of music do you listen to? (Please list a series of MBIDs as examples.)
I listen to Punajbi, English and Hindi music.
Here are MBID’s of some of my favourite artists:-
Name: Karan Aujla
MBID: 4a779683-5404-4b90-a0d7-242495158265
Name: Sam Smith
MBID: 5a85c140-dcf9-4dd2-b2c8-aff0471549f3
Name: Imagine Dragons
MBID: 012151a8-0f9a-44c9-997f-ebd68b5389f9
What aspects of the project you’re applying for (e.g., MusicBrainz, AcousticBrainz, etc.) interest you the most?
Music is an essential part of my life, and I listen to it while doing everything. It is particularly crucial for me when I work out at the gym, which I do daily. As a software engineer who works on computer allot, I rely on Apple Music, and I wanted to integrate it with Listenbrainz, which supports various other platforms. Therefore, this project is a perfect blend of my favorite things.
Have you ever used MusicBrainz to tag your files?
Not much but I have heard allot about it from my friends at univeristy.
Have you contributed to other Open Source projects? If so, which projects and can we see some of your code?
I have previously made contributions to ListenBrainz, as well as to an open-source project called Capstone Dashboard which is available on GitHub at Capstone-dashboard-open-UofA(https://github.com/open-uofa/capstone-course-dashboard
).
What sorts of programming projects have you done on your own time?
I am conducting NLP research utilizing transformers, and have also applied my knowledge in PCGML and unity to develop a no-code platform for creating 2D games without programming. In addition, I have completed contract work involving MERN techstack to deliver websites, created my own portfolio using React, and have dabbled in Flutter to develop apps for leisure. Additionally, I have experimented with Flask. For more examples of my work, please visit my Github profile.