Skip to content
Snippets Groups Projects

Problem 4: Implementing Spotify using actors

Setup

Use the following commands to make a fresh clone of your repository:

git clone -b concpar22final04 git@gitlab.epfl.ch:lamp/student-repositories-s22/cs206-GASPAR.git concpar22final04

If you have issues with the IDE, try reimporting the build, if you still have problems, use compile in sbt instead.

Useful Links

Overview

In this exercise, you will implement the core functionalities of an online music streaming service. Users will be modelled as individual Akka actors.

  • Each user has a unique identifier and a name.
  • Each user can like and unlike songs (stored in the user's liked songs list). Liked songs are sorted by reverse date of liking time (the last liked song must be the first element of the list). Elements of this list must be unique: a song can only be liked once. Liking a song twice should not impact the order.
  • Each user can subscribe and unsubscribe to other users to see what they are listening to. This is stored in the user's activity feed. The items in the activity feed are sorted by reverse date of activity time (the last added activity must be the first element of the list). Items in this list should be unique by user. If a new activity with a user that is already in the list is added, the former should be removed, so that we always see the latest activity for each user we have subscribed to. You can assume that there is a single actor and ActorRef per user.

This corresponds to the core features of Spotify:

Your task is to implement the receive method of the User actor. See the enums in the User what messages and responses a User should handle.

You are allowed to add private methods and attributes to the User class. You can also import any Scala collection you might find useful.

To implement the last part (problem 4.4), you will need to interact with the SongStore actor passed as the songStore parameter. You do not need it for the other parts.

Problem 4.1: Getting user info (50 points)

Your first task is to implement the User.receive method so that it handles the GetInfo and GetHomepageData messages. This will allow you to pass the first 2 tests.

Problem 4.2: Updating user info (70 points)

Your second task is to expand User.receive so that it handles the Like and Unlike messages.

Problem 4.3: Updating user info (70 points)

Your third task is to expand User.receive so that it handles the Subscribe, Unsubscribe, AddActivity and Play messages.

Problem 4.4: Displaying the homepage (60 points)

Your last (but not least!) task is to expand User.receive so that it handles the GetHomepageText message.

A GetHomepageText should be answered with HomepageText message. Here is an example of a HomepageText.result:

Howdy Ada!

Liked Songs:
* Sunny by Boney M.
* J'irai où tu iras by Céline Dion & Jean-Jacques Goldman
* Hold the line by TOTO

Activity Feed:
* Bob is listening to Straight Edge by Minor Threat
* Donald is listening to Désenchantée by Mylène Farmer
* Carol is listening to Breakfast in America by Supertramp

More precisely, it should contains the following lines in order:

  1. Howdy $name!, where $name is the name of the recipient user.
  2. A blank line
  3. Liked Songs:
  4. Zero or more lines listing the user's liked songs. Each of these lines should be of the form "* ${song.title} by ${song.artist}, where ${song.title} is the title of the song and ${song.artist} its artist.
  5. A blank line
  6. Zero or more lines listing the user activity feed items. Each of these lines should be of the form * ${user.name} is listening to ${song.title} by ${song.artist}, where ${user.name} is the name of the user listening, ${song.title} is the title of the song and ${song.artist} its artist.

In order to fetch the songs information (titles and artists), you should use the songStore actor passed as an argument to User. See the enums in the SongsStore companion object to learn how to interact with the song store.

Hint 1: to construct the result, you might find useful to use f-strings and the List.mkString method. Here is an example of how to use them:

val fruits = List("Banana", "Apple", "Kiwi")
val result = f"""Fruits:
${fruits.map(fruit => f"* ${fruit}").mkString("\n")}"""

assert(result == """Fruits:
* Banana
* Apple
* Kiwi""")

Hint 2: if you need to send the result of a future to an actor, you should use the pipeTo method as described in the lectures and here.