Skip to content

Commit

Permalink
feat: ability to manage albums from the user's library
Browse files Browse the repository at this point in the history
  • Loading branch information
browniebroke committed Oct 9, 2021
1 parent ac0de0e commit 45f9bba
Show file tree
Hide file tree
Showing 7 changed files with 274 additions and 2 deletions.
32 changes: 31 additions & 1 deletion deezer/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
Implements a client class to query the
`Deezer API <https://developers.deezer.com/api>`_
"""
from typing import List, Optional

import requests

from deezer.exceptions import DeezerErrorResponse, DeezerHTTPError
Expand Down Expand Up @@ -185,7 +187,7 @@ def rate_album(self, album_id: str, note: int) -> bool:
The note should be and integer between 1 and 5.
:returns: boolean
:returns: boolean whether rating was applied
"""
return self.request("POST", f"album/{album_id}", note=note)

Expand Down Expand Up @@ -293,6 +295,34 @@ def get_user(self, object_id):
"""
return self.get_object("user", object_id)

def get_user_albums(self, user_id: Optional[int] = None) -> List[Album]:
"""
Get the favourites albums for the given user_id if provided or current user if not.
:param user_id: the user ID to get favourites albums.
:return: a list of :class:`~deezer.resources.Album` instances.
"""
user_id_str = str(user_id) if user_id else "me"
return self.request("GET", f"user/{user_id_str}/albums")

def add_user_album(self, album_id: int) -> bool:
"""
Add an album to the user's library
:param album_id: the ID of the album to add.
:return: boolean whether the operation succeeded.
"""
return self.request("POST", "user/me/albums", album_id=album_id)

def remove_user_album(self, album_id: int) -> bool:
"""
Remove an album from the user's library
:param album_id: the ID of the album to remove.
:return: boolean whether the operation succeeded.
"""
return self.request("DELETE", "user/me/albums", album_id=album_id)

def search(self, query, relation=None, index=0, limit=25, **kwargs):
"""
Search track, album, artist or user
Expand Down
2 changes: 1 addition & 1 deletion deezer/resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ def get_albums(self, **kwargs):
:returns: list of :mod:`Album <deezer.resources.Album>` instances
"""
return self.get_relation("albums", **kwargs)
return self.client.get_user_albums(self.id)

def iter_albums(self, **kwargs):
"""
Expand Down
54 changes: 54 additions & 0 deletions tests/cassettes/TestClient.test_add_user_album.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
interactions:
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- identity
Connection:
- keep-alive
Content-Length:
- '0'
User-Agent:
- python-requests/2.26.0
method: POST
uri: https://api.deezer.com/user/me/albums?album_id=302127&access_token=dummy
response:
body:
string: 'true'
headers:
Access-Control-Allow-Credentials:
- 'true'
Access-Control-Allow-Headers:
- X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding
Access-Control-Allow-Methods:
- POST, GET, OPTIONS, DELETE, PUT
Access-Control-Expose-Headers:
- Location
Access-Control-Max-Age:
- '86400'
Cache-Control:
- no-store, no-cache, must-revalidate
Connection:
- keep-alive
Content-Length:
- '4'
Content-Type:
- application/json; charset=utf-8
P3P:
- policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA"
Pragma:
- no-cache
Server:
- Apache
X-Content-Type-Options:
- nosniff
X-Host:
- blm-web-158
x-org:
- FR
status:
code: 200
message: OK
version: 1
56 changes: 56 additions & 0 deletions tests/cassettes/TestClient.test_get_user_albums[args0].yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
interactions:
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- identity
Connection:
- keep-alive
User-Agent:
- python-requests/2.26.0
method: GET
uri: https://api.deezer.com/user/me/albums?access_token=dummy
response:
body:
string: '{"data":[{"id":357404,"title":"OK Cowboy","link":"https:\/\/www.deezer.com\/album\/357404","cover":"https:\/\/api.deezer.com\/album\/357404\/image","cover_small":"https:\/\/cdns-images.dzcdn.net\/images\/cover\/c35a9579e79da78be284094d5cca571b\/56x56-000000-80-0-0.jpg","cover_medium":"https:\/\/cdns-images.dzcdn.net\/images\/cover\/c35a9579e79da78be284094d5cca571b\/250x250-000000-80-0-0.jpg","cover_big":"https:\/\/cdns-images.dzcdn.net\/images\/cover\/c35a9579e79da78be284094d5cca571b\/500x500-000000-80-0-0.jpg","cover_xl":"https:\/\/cdns-images.dzcdn.net\/images\/cover\/c35a9579e79da78be284094d5cca571b\/1000x1000-000000-80-0-0.jpg","md5_image":"c35a9579e79da78be284094d5cca571b","nb_tracks":13,"release_date":"2005-04-04","record_type":"album","available":true,"tracklist":"https:\/\/api.deezer.com\/album\/357404\/tracks","explicit_lyrics":false,"time_add":1338501600,"artist":{"id":641,"name":"Vitalic","picture":"https:\/\/api.deezer.com\/artist\/641\/image","picture_small":"https:\/\/cdns-images.dzcdn.net\/images\/artist\/5e970ce2f5c52fe2b036f088eb4a7bef\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/cdns-images.dzcdn.net\/images\/artist\/5e970ce2f5c52fe2b036f088eb4a7bef\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/cdns-images.dzcdn.net\/images\/artist\/5e970ce2f5c52fe2b036f088eb4a7bef\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/cdns-images.dzcdn.net\/images\/artist\/5e970ce2f5c52fe2b036f088eb4a7bef\/1000x1000-000000-80-0-0.jpg","tracklist":"https:\/\/api.deezer.com\/artist\/641\/top?limit=50","type":"artist"},"type":"album"},{"id":230721992,"title":"Tank
(Remastered)","link":"https:\/\/www.deezer.com\/album\/230721992","cover":"https:\/\/api.deezer.com\/album\/230721992\/image","cover_small":"https:\/\/cdns-images.dzcdn.net\/images\/cover\/0a52a069f4596c26c6f5b1527c8814c1\/56x56-000000-80-0-0.jpg","cover_medium":"https:\/\/cdns-images.dzcdn.net\/images\/cover\/0a52a069f4596c26c6f5b1527c8814c1\/250x250-000000-80-0-0.jpg","cover_big":"https:\/\/cdns-images.dzcdn.net\/images\/cover\/0a52a069f4596c26c6f5b1527c8814c1\/500x500-000000-80-0-0.jpg","cover_xl":"https:\/\/cdns-images.dzcdn.net\/images\/cover\/0a52a069f4596c26c6f5b1527c8814c1\/1000x1000-000000-80-0-0.jpg","md5_image":"0a52a069f4596c26c6f5b1527c8814c1","nb_tracks":14,"release_date":"2005-02-28","record_type":"album","available":true,"tracklist":"https:\/\/api.deezer.com\/album\/230721992\/tracks","explicit_lyrics":false,"time_add":1633799308,"artist":{"id":1422,"name":"Asian
Dub Foundation","picture":"https:\/\/api.deezer.com\/artist\/1422\/image","picture_small":"https:\/\/cdns-images.dzcdn.net\/images\/artist\/8c4fda02331e9e658877b28b0b7a3115\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/cdns-images.dzcdn.net\/images\/artist\/8c4fda02331e9e658877b28b0b7a3115\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/cdns-images.dzcdn.net\/images\/artist\/8c4fda02331e9e658877b28b0b7a3115\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/cdns-images.dzcdn.net\/images\/artist\/8c4fda02331e9e658877b28b0b7a3115\/1000x1000-000000-80-0-0.jpg","tracklist":"https:\/\/api.deezer.com\/artist\/1422\/top?limit=50","type":"artist"},"type":"album"}],"checksum":"2c515a429cac2d4b5da8b37d75a093ba","total":2}'
headers:
Access-Control-Allow-Credentials:
- 'true'
Access-Control-Allow-Headers:
- X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding
Access-Control-Allow-Methods:
- POST, GET, OPTIONS, DELETE, PUT
Access-Control-Expose-Headers:
- Location
Access-Control-Max-Age:
- '86400'
Cache-Control:
- no-store, no-cache, must-revalidate
Connection:
- keep-alive
Content-Length:
- '3230'
Content-Type:
- application/json; charset=utf-8
P3P:
- policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA"
Pragma:
- no-cache
Server:
- Apache
Vary:
- Accept-Encoding
X-Content-Type-Options:
- nosniff
X-Host:
- blm-web-27
x-org:
- FR
status:
code: 200
message: OK
version: 1
56 changes: 56 additions & 0 deletions tests/cassettes/TestClient.test_get_user_albums[args1].yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
interactions:
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- identity
Connection:
- keep-alive
User-Agent:
- python-requests/2.26.0
method: GET
uri: https://api.deezer.com/user/359622/albums?access_token=dummy
response:
body:
string: '{"data":[{"id":357404,"title":"OK Cowboy","link":"https:\/\/www.deezer.com\/album\/357404","cover":"https:\/\/api.deezer.com\/album\/357404\/image","cover_small":"https:\/\/cdns-images.dzcdn.net\/images\/cover\/c35a9579e79da78be284094d5cca571b\/56x56-000000-80-0-0.jpg","cover_medium":"https:\/\/cdns-images.dzcdn.net\/images\/cover\/c35a9579e79da78be284094d5cca571b\/250x250-000000-80-0-0.jpg","cover_big":"https:\/\/cdns-images.dzcdn.net\/images\/cover\/c35a9579e79da78be284094d5cca571b\/500x500-000000-80-0-0.jpg","cover_xl":"https:\/\/cdns-images.dzcdn.net\/images\/cover\/c35a9579e79da78be284094d5cca571b\/1000x1000-000000-80-0-0.jpg","md5_image":"c35a9579e79da78be284094d5cca571b","nb_tracks":13,"release_date":"2005-04-04","record_type":"album","available":true,"tracklist":"https:\/\/api.deezer.com\/album\/357404\/tracks","explicit_lyrics":false,"time_add":1338501600,"artist":{"id":641,"name":"Vitalic","picture":"https:\/\/api.deezer.com\/artist\/641\/image","picture_small":"https:\/\/cdns-images.dzcdn.net\/images\/artist\/5e970ce2f5c52fe2b036f088eb4a7bef\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/cdns-images.dzcdn.net\/images\/artist\/5e970ce2f5c52fe2b036f088eb4a7bef\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/cdns-images.dzcdn.net\/images\/artist\/5e970ce2f5c52fe2b036f088eb4a7bef\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/cdns-images.dzcdn.net\/images\/artist\/5e970ce2f5c52fe2b036f088eb4a7bef\/1000x1000-000000-80-0-0.jpg","tracklist":"https:\/\/api.deezer.com\/artist\/641\/top?limit=50","type":"artist"},"type":"album"},{"id":230721992,"title":"Tank
(Remastered)","link":"https:\/\/www.deezer.com\/album\/230721992","cover":"https:\/\/api.deezer.com\/album\/230721992\/image","cover_small":"https:\/\/cdns-images.dzcdn.net\/images\/cover\/0a52a069f4596c26c6f5b1527c8814c1\/56x56-000000-80-0-0.jpg","cover_medium":"https:\/\/cdns-images.dzcdn.net\/images\/cover\/0a52a069f4596c26c6f5b1527c8814c1\/250x250-000000-80-0-0.jpg","cover_big":"https:\/\/cdns-images.dzcdn.net\/images\/cover\/0a52a069f4596c26c6f5b1527c8814c1\/500x500-000000-80-0-0.jpg","cover_xl":"https:\/\/cdns-images.dzcdn.net\/images\/cover\/0a52a069f4596c26c6f5b1527c8814c1\/1000x1000-000000-80-0-0.jpg","md5_image":"0a52a069f4596c26c6f5b1527c8814c1","nb_tracks":14,"release_date":"2005-02-28","record_type":"album","available":true,"tracklist":"https:\/\/api.deezer.com\/album\/230721992\/tracks","explicit_lyrics":false,"time_add":1633799308,"artist":{"id":1422,"name":"Asian
Dub Foundation","picture":"https:\/\/api.deezer.com\/artist\/1422\/image","picture_small":"https:\/\/cdns-images.dzcdn.net\/images\/artist\/8c4fda02331e9e658877b28b0b7a3115\/56x56-000000-80-0-0.jpg","picture_medium":"https:\/\/cdns-images.dzcdn.net\/images\/artist\/8c4fda02331e9e658877b28b0b7a3115\/250x250-000000-80-0-0.jpg","picture_big":"https:\/\/cdns-images.dzcdn.net\/images\/artist\/8c4fda02331e9e658877b28b0b7a3115\/500x500-000000-80-0-0.jpg","picture_xl":"https:\/\/cdns-images.dzcdn.net\/images\/artist\/8c4fda02331e9e658877b28b0b7a3115\/1000x1000-000000-80-0-0.jpg","tracklist":"https:\/\/api.deezer.com\/artist\/1422\/top?limit=50","type":"artist"},"type":"album"}],"checksum":"2c515a429cac2d4b5da8b37d75a093ba","total":2}'
headers:
Access-Control-Allow-Credentials:
- 'true'
Access-Control-Allow-Headers:
- X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding
Access-Control-Allow-Methods:
- POST, GET, OPTIONS, DELETE, PUT
Access-Control-Expose-Headers:
- Location
Access-Control-Max-Age:
- '86400'
Cache-Control:
- no-store, no-cache, must-revalidate
Connection:
- keep-alive
Content-Length:
- '3230'
Content-Type:
- application/json; charset=utf-8
P3P:
- policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA"
Pragma:
- no-cache
Server:
- Apache
Vary:
- Accept-Encoding
X-Content-Type-Options:
- nosniff
X-Host:
- blm-web-150
x-org:
- FR
status:
code: 200
message: OK
version: 1
54 changes: 54 additions & 0 deletions tests/cassettes/TestClient.test_remove_user_album.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
interactions:
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- identity
Connection:
- keep-alive
Content-Length:
- '0'
User-Agent:
- python-requests/2.26.0
method: DELETE
uri: https://api.deezer.com/user/me/albums?album_id=302127&access_token=dummy
response:
body:
string: 'true'
headers:
Access-Control-Allow-Credentials:
- 'true'
Access-Control-Allow-Headers:
- X-Requested-With, Content-Type, Authorization, Origin, Accept, Accept-Encoding
Access-Control-Allow-Methods:
- POST, GET, OPTIONS, DELETE, PUT
Access-Control-Expose-Headers:
- Location
Access-Control-Max-Age:
- '86400'
Cache-Control:
- no-store, no-cache, must-revalidate
Connection:
- keep-alive
Content-Length:
- '4'
Content-Type:
- application/json; charset=utf-8
P3P:
- policyref="/w3c/p3p.xml" CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA"
Pragma:
- no-cache
Server:
- Apache
X-Content-Type-Options:
- nosniff
X-Host:
- blm-web-25
x-org:
- FR
status:
code: 200
message: OK
version: 1
22 changes: 22 additions & 0 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,28 @@ def test_no_user_raise(self, client):
with pytest.raises(ValueError):
client.get_user(-1)

@pytest.mark.parametrize(
"args",
[
(),
(359622,),
],
)
def test_get_user_albums(self, client_token, args):
user_albums = client_token.get_user_albums(*args)
assert len(user_albums) == 2
assert all(isinstance(a, deezer.resources.Album) for a in user_albums)
assert user_albums[0].title == "OK Cowboy"
assert user_albums[1].title == "Tank (Remastered)"

def test_add_user_album(self, client_token):
result = client_token.add_user_album(302127)
assert result is True

def test_remove_user_album(self, client_token):
result = client_token.remove_user_album(302127)
assert result is True

def test_options_1(self, client):
"""Test a query with extra arguments"""
result = client.search("Billy Jean", limit=2)
Expand Down

0 comments on commit 45f9bba

Please sign in to comment.