GSOC 2021: Short CB Reviews & Pin Recordings

This proposal consists of two of the smaller sized projects suggested on the MusicBrainz Wiki.

Personal Information

  • Nickname: jasondk
  • IRC Nick: jasondk
  • MB username: whirlwind
  • Github: jdaok
  • Time Zone: UTC-8

Create short music reviews on ListenBrainz using the CritiqueBrainz API

Project Overview

ListenBrainz should allow users to write short reviews for tracks, release groups, and artists directly through our website using the CritiqueBrainz API. This project aims to drive increased contributions of reviews to CritiqueBrainz by making it easier to submit them. Reviews submitted through this feature should also populate the ListenBrainz feed.

Project Description + Mockups

Users will be able to write reviews through the ‘My Listens’ page.

The user will be prompted to select the entity to review and write the review directly from ListenBrainz.


Users will be able to view reviews that they and their followers wrote on their User Feed.

(clicking on the review card will open the full review on CritiqueBrainz)

Implementation

Submitting Reviews

The user will first authorize ListenBrainz to post reviews with their CritiqueBrainz account through OAuth 2 https://critiquebrainz.readthedocs.io/api/oauth2.html (The cb_auth_token will be stored in the “user” table).

*OAuth integration across MetaBrainz is planned for the future which will make authenticating by logging into CB unnecessary. We will have a better idea of how this will work as this change gets worked on and I will adjust accordingly.

Submitting reviews requires the entity MBID which can be retrieved with the track metadata:

Release Group MBID

Lastfm_release_mbid: must lookup RELEASE_GROUP_MBID using MB API.

Otherwise, must lookup release MBID using LABS API (mbid-mapping),
then lookup RELEASE_GROUP_MBID using MB API.

Artist MBID

Lastfm_artist_mbid: can be used as is.

Artist_msid: must lookup artist MBID using LABS API (artist-credit-from-artist-msid)

Recording MBID

Must lookup recording MBID using LABS API (mbid-mapping).

submitReviewToCB()will be added to send post requests to CB’s API.

*By the time GSoC begins, all listens will likely be attached to an MBID. In this case the above steps would be unnecessary. If a listen didn’t have an MBID, the user would be notified and prompted to the CB website to search for it there instead.

Fetching Reviews to Display On User Feed
After the call to submit the review to CB is successful the review will be stored as a timeline event for displaying on the userfeed. The metadata would look like this:

"entity_id": <UUID of the entity that is being reviewed>,
"entity_name": <Name of the entity (track name, recording group name..)>,
"entity_type": <Type of the entity (track? recording group? artist?>,
"review_text": <The text content of the review>

get_cb_review_events_for_feed will fetch the reviews from the database and return them as events to the feed/events endpoint where they will be sorted and displayed with all timeline events.

CB Reviews vs LB Reviews
Reviews submitted through this new feature would be different in length and less formal than reviews that already exist on CritiqueBrainz. These reviews will use the "source" field that already exists in CB’s review schema in to indicate that they were submitted from LB.

For this CB needs to be modified to set “ListenBrainz” as the source when it recieves the post request from LB. Post GSoC, we should indicate if reviews were submitted through LB on the website.

Pin Favorite Recordings to Profile

Project Overview

Currently all a user can do to show their love for a recording is to “love” it or recommend it. This project aims to allow for ListenBrainz users to pin their favorite recording to their profiles with a short blurb. This feature will allow users to get a better idea of what kind of music their following circle is interested in and make user profiles more personalized.

Project Description + Mockups

Users will be able to pin recordings from their ‘My Listens’ page.

enter image description here

The Pin Track modal will look like this: (Users will only be able to have one track pinned at a time and the typed blurb will be capped at 280 characters.)

When visiting other profiles the pinned track will display at the top of the ‘Listens’ tab.

Also all tracks the user has ever pinned will be displayed on listenbrainz.org/user/username/pinned-tracks

(Tracks can be deleted from this tab as well).

On the User Feed: if anyone the User is following has a recording pinned, it will appear under the BrainzPlayer.

enter image description here

(one pinned recording will display per user).

Implementation

Database

A separate Postgres table will store pinned tracks:

pinned_track
Column Type Nullable Default
id SERIAL
user_id INTEGER not null
recording_mbid UUID not null
blurb_content VARCHAR
currently_pinned BOOLEAN not null TRUE
created TIMESTAMP (w/ tz) not null NOW()
  • User_id will reference id from the “user” table
  • We can use labs API to retrieve the track data from the recording_mbid for displaying pinned tracks

API + SQL

The new API routes and corresponding SQL queries will look like this:

Pin a track to the user’s profile and submit it to the server:

POST /user/<user_id>/pin-track {
    recording_mbid: a19665c3-fa16-4974-96ca-192d3156381c
    blurb_content: ‘My favorite song right now!’ 
} 

UPDATE pinned_track SET currently_pinned = FALSE WHERE user_id = :user_id AND currently_pinned = TRUE
INSERT INTO pinned_track (user_id, recording_mbid, blurb_content, created)
// Returns response status

Unpin a track from the user’s profile and update the server:

POST /user/<user_id>/unpin-track {
}

UPDATE pinned_track SET currently_pinned = FALSE WHERE user_id = :user_id AND currently_pinned = TRUE
// Returns response status

Get the pinned track for a single user

GET /user/<user_id>/get-user-pin {
}

SELECT (user_id, recording_mbid, blurb_content, created) FROM pinned_track WHERE user_id = :user_id AND currently_pinned = TRUE

// Returns json with one pinned track or none
{
  count: 1,
  pinned_track: [
    {
        "user_id": UserID
        "recording_mbid": "a19665c3-fa16-4974-96ca-192d3156381c",
        "created": 123456789
        "blurb_content": "This song is awesome!"
    },
    ....
  ],
  offset: 0,
  total_count: 1
}

Get the pinned tracks of every user in a user’s following list

GET /user/<user_id>/get-user-following-pins {
}

// Will iterate over a list of musicbrainz ID’s.
SELECT (user_id, recording_mbid, blurb_content, created) FROM pinned_track WHERE user_id = :user_id AND currently_pinned = TRUE

// Returns json with pinned tracks or none
{
  count: 3,
  pinned_track: [
    {
        "user_id": UserOne
        "recording_mbid": "a19665c3-fa16-4974-96ca-192d3156381c",
        "created": 123456789
        "blurb_content": "This song is awesome!"
    },
    ....
  ],
  offset: 0,
  total_count: 3
}

Get the pinned track history of a user

GET /user/<user_id>/get-user-pin-history {
}

SELECT (user_id, recording_mbid, blurb_content, currently_pinned, created)  FROM pinned_track WHERE user_id = :user_id

// Returns json with pinned tracks or none
{
  count: 3,
  pinned_track: [
    {
        "user_id": UserOne
        "recording_mbid": "a19665c3-fa16-4974-96ca-192d3156381c",
        "created": 123456789
        "blurb_content": "This song is awesome!"
        "currently_pinned": "false"
    },
    ....
  ],
  offset: 0,
  total_count: 3
}

Delete a track from pinned history

POST /user/<user_id>/delete-pin {
    recording_mbid: a19665c3-fa16-4974-96ca-192d3156381c
    created: 1617436332
}

DELETE FROM pinned_track WHERE user_id = :user_id AND recording_mbid= :recording_mbidAND created = :created
// Returns response status

Timeline

*all tests will be written alongside the code

Pre-Community Bonding Period

I plan to work on tickets and features to get more experience with the workflow, code structure, and related technical skills .

Community Bonding Period

  • Make sure there is a clear roadmap defined for both projects
  • Modify CB to set review’s source to “ListenBrainz” if review is from LB
  • Potentially begin week 1 early

Week 1: Begin Pin Recording project: Implement pinned_track schema and write SQL queries .
Week 2: Implement pinned track API endpoints for the SQL queries.
Week 3: Implement UI components pin track modal and pinned track card.
Week 4: Implement UI components pin track history tab and pinned tracks list in user feed.
Week 5: Buffer period to finish project and testing. Otherwise continue first evaluation

Week 6: Begin CritiqueBrainz project: Implement CB OAuth.
Week 7: Create the “Write Review” modal (frontend only this week)
Week 8: Complete the submitReviewToCB() function to make modal functional
Week 9: Complete the reviews displaying on user event feed. (UI + DB changes)
Week 10: Buffer period to catch up if behind. Then finish writing documentation and fixing bugs or start on stretch goals. final evaluation!

Stretch Goals / Post-GSOC Ideas :

  • “Reviews” tab that shows user’s CritiqueBrainz reviews on LB profiles
  • User feed customization: allow users to filter which events they want to see on their feed (only display listens, reviews, etc.)

Information About Me

I am a sophomore studying computer science who will be attending University of California, Davis in the fall. I started contributing to ListenBrainz in January.

Tell us about the computer(s) you have available for working on your GSoC project!

I have a desktop computer running Windows 10 with an Intel i7 processor and 8 GB RAM.

When did you first start programming?

My first experiences programming were making ROBLOX games in middle school (but I only started to take programming seriously in high school).

What type of music do you listen to? (Please list a series of MBIDs as examples.)

I listen to a lot of experimental pop music and R&B. I also like electronic and dance music.

What aspects of the project you’re applying for (e.g., MusicBrainz, AcousticBrainz, etc.) interest you the most?

I’m an obsessive tracker of my listening history, I love music, metadata, and collecting information about my listening habits! The music data that LB collects is public which is useful in building tools that improve music recommendation technologies. The ListenBrainz project stands out because it’s open source and any passionate individual can contribute.

Have you ever used MusicBrainz to tag your files?

I haven’t used MusicBrainz to tag my files yet.

Have you contributed to other Open Source projects? If not, do you have other code we can look at?

I am new to open source but many of my projects can be found on my GitHub.

What sorts of programming projects have you done on your own time?
I have made a lot of interactive websites as portfolios or shops or calculators.

How much time do you have available, and how would you plan to use it?

I will likely be able to dedicate 40+ hours to GSoC each week.

Do you plan to have a job or study during the summer in conjunction with Summer of Code?

I will be working part time on the weekends but it should not interfere with my availability for GSoC significantly, and I will be out of school and available for the entire duration of the program.

5 Likes

Overall, this looks really good! I have a few general comments.

  • The OAuth to CB is not ideal, but probably what we should do for this project. Ideally, we’d just need to OAuth to metabrainz.org once and use that to recognize users, but that’s a separate project. I would get in touch with lucifer and ruaok about it though.

  • Another thing I’d like to see is more thinking about failure modes. For example, what happens if the labs API isn’t able to map a msid to an MBID, how do we surface that to the user?

  • It’d be great if we could have something in CB (maybe a source field) which we can later use to recognize which reviews come from LB.

  • We should also think about metrics we want to track and such. This might the perfect place to start using brainzutils metrics system in LB. I would love to see graphs of number of reviews added and such.

  • Would love for the timeline to clearly mark deliverables for each week or such, I think that really helps when actually working on the project. Also, try to think of this in a way that things can be released every week or so. I’d prefer that instead of two huge releases each during an evalution period.

More specific implementation related comment:

I don’t think we’ll want to hit the CB API to get feed events, we should store them in the user_timeline_event table.

3 Likes

Thanks for the very nice proposal – looking quite nice!

As to iliekcomputers’s comment: I hope that by the time GSoC starts up, we can get rid of MSIDs and have MBIDs for listens. I’d even be ok, saying that if a listen didn’t have an MBID that we could not review it. If this pans out, you have less work to do.

For the pinned track schema:

track_metadata will contain recording_msid, artist/release/track name, and additional info.

This would duplicate data that we shouldn’t duplicate. Ideally we would only store recording_mbid and nothing else, since we can fetch that from MB database.

A better URL would be /user//pin-track and not pass the user in the POST body. (Same for unpinning). Also please adapt the other URLs to fit the scheme better. Also it would be nice to see what JSON is returned by these endpoints, but it isn’t super critical.

I’ll also echo Param’s comment about adding milestones – would be nice to see. And also, if you are able to start working sooner on this project, then this may remove some time pressure on you. You’ve got quite a lot of things on your plate – doable, but a lot.

I’m looking forward to your final proposal!

2 Likes

Thank you again for the feedback @iliekcomputers @rob . I made the changes to the proposal and updated here just in case, and I submitted it to the GSoC page as well.

Would the idea for the metrics and graphs of number of reviews added maybe fit more on CritiqueBrainz than on ListenBrainz?

I hope the timeline looks better, and I switched the order of the two projects around. Also, any additional feedback on if I am giving myself too much or too little for any of the tasks on the timeline is appreciated!

I agree, I will try to begin at least a week earlier to give myself more time. I would still be completing everything in the same order as above. :slight_smile:

Sorry if I was unclear here, I meant we should have metrics for our observability (probably in Grafana). In the end this will probably just be a case of sprinkling a few Metrics.increment('number_of_reviews_created_by_lb) and similar across the new code and then making sure those graphs show up well on stats.metabrainz.org.

See this PR for context on what I want to use: BU-34: Simple metrics system by alastair ¡ Pull Request #43 ¡ metabrainz/brainzutils-python ¡ GitHub