Skip to content

Commit b89fb87

Browse files
committed
Update playlist_api after merge conflict resolution
Re-implemented the changes from previous commits after a clean-slate sort of conflict resolution, as a partial merge would have been too messy.
1 parent c1bc257 commit b89fb87

File tree

1 file changed

+76
-20
lines changed

1 file changed

+76
-20
lines changed

listenbrainz/webserver/views/playlist_api.py

+76-20
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@
1212
import listenbrainz.db.user as db_user
1313
from listenbrainz.domain.spotify import SpotifyService, SPOTIFY_PLAYLIST_PERMISSIONS
1414
from listenbrainz.domain.apple import AppleService
15-
from listenbrainz.troi.export import export_to_spotify, export_to_apple_music
16-
from listenbrainz.troi.import_ms import import_from_spotify, import_from_apple_music
15+
from listenbrainz.domain.soundcloud import SoundCloudService
16+
from listenbrainz.troi.export import export_to_spotify, export_to_apple_music, export_to_soundcloud
17+
from listenbrainz.troi.import_ms import import_from_spotify, import_from_apple_music, import_from_soundcloud
1718
from listenbrainz.webserver import db_conn, ts_conn
1819
from listenbrainz.metadata_cache.apple.client import Apple
19-
20+
from listenbrainz.metadata_cache.soundcloud.client import SoundCloud
2021
from listenbrainz.webserver.utils import parse_boolean_arg
2122
from listenbrainz.webserver.decorators import crossdomain, api_listenstore_needed
2223
from listenbrainz.webserver.errors import APIBadRequest, APIInternalServerError, APINotFound, APIForbidden, APIError, PlaylistAPIXMLError, APIUnauthorized
@@ -869,15 +870,18 @@ def export_playlist(playlist_mbid, service):
869870
if not is_valid_uuid(playlist_mbid):
870871
log_raise_400("Provided playlist ID is invalid.")
871872

872-
if service != "spotify" and service != "apple_music":
873-
raise APIBadRequest(f"Service {service} is not supported. We currently only support 'spotify' and 'apple_music'.")
873+
if service != "spotify" and service != "apple_music" and service != "soundcloud":
874+
raise APIBadRequest(f"Service {service} is not supported. We currently only support 'spotify', 'apple_music' and 'soundcloud'.")
874875

875876
if service == "spotify":
876877
spotify_service = SpotifyService()
877878
token = spotify_service.get_user(user["id"], refresh=True)
878879
elif service == "apple_music":
879880
apple_service = AppleService()
880881
token = apple_service.get_user(user["id"])
882+
elif service == "soundcloud":
883+
soundcloud_service = SoundCloudService()
884+
token = soundcloud_service.get_user(user["id"])
881885

882886
if not token:
883887
raise APIBadRequest(f"Service {service} is not linked. Please link your {service} account first.")
@@ -893,6 +897,8 @@ def export_playlist(playlist_mbid, service):
893897
url = export_to_spotify(user["auth_token"], token["access_token"], is_public, playlist_mbid=playlist_mbid)
894898
elif service == "apple_music":
895899
url = export_to_apple_music(user["auth_token"], token["access_token"], token["refresh_token"], is_public, playlist_mbid=playlist_mbid)
900+
elif service == "soundcloud":
901+
url = export_to_soundcloud(user["auth_token"], token["access_token"], is_public, playlist_mbid=playlist_mbid)
896902
return jsonify({"external_url": url})
897903
except requests.exceptions.HTTPError as exc:
898904
error = exc.response.json()
@@ -917,22 +923,32 @@ def import_playlist_from_music_service(service):
917923
:resheader Content-Type: *application/json*
918924
"""
919925
user = validate_auth_header()
926+
supported_services = {
927+
"spotify": SpotifyService,
928+
"apple_music": AppleService,
929+
"soundcloud": SoundCloudService
930+
}
931+
932+
if service not in supported_services:
933+
raise APIBadRequest(f"Service {service} is not supported. "
934+
f"Supported services are: 'spotify', 'apple_music', 'soundcloud'.")
920935

921-
if service != "apple_music" and service != "spotify":
922-
raise APIBadRequest(f"Service {service} is not supported. We currently only support 'spotify'.")
936+
service_class = supported_services[service]()
923937

924938
if service == "spotify":
925-
spotify_service = SpotifyService()
926-
token = spotify_service.get_user(user["id"], refresh=True)
939+
token = service_class.get_user(user["id"], refresh=True)
927940
elif service == "apple_music":
928-
apple_service = AppleService()
929941
# TODO: implement refresh token for AppleMusic
942+
apple_service = AppleService()
930943
token = apple_service.get_user(user["id"])
944+
elif service == "soundcloud":
945+
soundcloud_service = SoundCloudService()
946+
token = soundcloud_service.get_user(user["id"])
931947

932948
if not token:
933949
raise APIBadRequest(f"Service {service} is not linked. Please link your {service} account first.")
934950

935-
if service == "apple_music" and not token["refresh_token"]:
951+
if service == "apple_music" and not token.get("refresh_token"):
936952
raise APIBadRequest("Not authorized to Apple Music. Please link your account first.")
937953

938954
if service == "spotify" and not SPOTIFY_PLAYLIST_PERMISSIONS.issubset(set(token["scopes"])):
@@ -942,12 +958,17 @@ def import_playlist_from_music_service(service):
942958

943959
try:
944960
if service == "spotify":
945-
sp = spotipy.Spotify(token["access_token"])
946-
playlists = sp.current_user_playlists()
961+
spotify = spotipy.Spotify(auth=token["access_token"])
962+
playlists = spotify.current_user_playlists()
947963
return jsonify(playlists["items"])
948-
else:
949-
apple = Apple().get_user_data("https://api.music.apple.com/v1/me/library/playlists/", token["refresh_token"])
950-
return jsonify(apple["data"])
964+
elif service == "apple_music":
965+
apple = Apple()
966+
playlists = apple.get_user_data("https://api.music.apple.com/v1/me/library/playlists/", token["refresh_token"])
967+
return jsonify(playlists.get("data", []))
968+
elif service == "soundcloud":
969+
soundcloud = SoundCloud(token["access_token"])
970+
playlists = soundcloud.get("https://api.soundcloud.com/me/playlists/")
971+
return jsonify(playlists)
951972
except requests.exceptions.HTTPError as exc:
952973
error = exc.response.json()
953974
raise APIError(error.get("error") or exc.response.reason, exc.response.status_code)
@@ -960,7 +981,7 @@ def import_playlist_from_music_service(service):
960981
def import_tracks_from_spotify_playlist(playlist_id):
961982
"""
962983
963-
Import a playlist tracks from a Spotify and convert them to JSPF.
984+
Import a playlist's tracks from Spotify and convert them to JSPF.
964985
965986
:reqheader Authorization: Token <user token>
966987
:param playlist_id: The Spotify playlist id to get the tracks from
@@ -996,7 +1017,7 @@ def import_tracks_from_spotify_playlist(playlist_id):
9961017
def import_tracks_from_apple_playlist(playlist_id):
9971018
"""
9981019
999-
Import a playlist tracks from a Apple Music and convert them to JSPF.
1020+
Import a playlist's tracks from Apple Music and convert them to JSPF.
10001021
10011022
:reqheader Authorization: Token <user token>
10021023
:param playlist_id: The Apple Music playlist id to get the tracks from
@@ -1025,6 +1046,36 @@ def import_tracks_from_apple_playlist(playlist_id):
10251046
raise APIError(error.get("error") or exc.response.reason, exc.response.status_code)
10261047

10271048

1049+
@playlist_api_bp.route("/soundcloud/<playlist_id>/tracks")
1050+
@crossdomain
1051+
@ratelimit()
1052+
@api_listenstore_needed
1053+
def import_tracks_from_soundcloud_playlist(playlist_id):
1054+
"""
1055+
Import a playlist's tracks from SoundCloud and convert them to JSPF.
1056+
:reqheader Authorization: Token <user token>
1057+
:param playlist_id: The SoundCloud playlist id to get the tracks from
1058+
:statuscode 200: tracks are fetched and converted.
1059+
:statuscode 401: invalid authorization. See error message for details.
1060+
:statuscode 404: Playlist not found
1061+
:resheader Content-Type: *application/json*
1062+
"""
1063+
user = validate_auth_header()
1064+
1065+
soundcloud_service = SoundCloudService()
1066+
token = soundcloud_service.get_user(user["id"])
1067+
1068+
if not token:
1069+
raise APIBadRequest(f"Service SoundCloud is not linked. Please link your SoundCloud account first.")
1070+
1071+
try:
1072+
playlist = import_from_soundcloud(token["access_token"], user["auth_token"], playlist_id)
1073+
return playlist
1074+
except requests.exceptions.HTTPError as exc:
1075+
error = exc.response.json()
1076+
raise APIError(error.get("error") or exc.response.reason, exc.response.status_code)
1077+
1078+
10281079
@playlist_api_bp.post("/export-jspf/<service>")
10291080
@crossdomain
10301081
@ratelimit()
@@ -1042,15 +1093,18 @@ def export_playlist_jspf(service):
10421093
"""
10431094
user = validate_auth_header()
10441095

1045-
if service != "spotify" and service != "apple_music":
1046-
raise APIBadRequest(f"Service {service} is not supported. We currently only support 'spotify' and 'apple_msuic'.")
1096+
if service != "spotify" and service != "apple_music" and service != "soundcloud":
1097+
raise APIBadRequest(f"Service {service} is not supported. We currently only support 'spotify', 'apple_music' and 'soundcloud'.")
10471098

10481099
if service == "spotify":
10491100
spotify_service = SpotifyService()
10501101
token = spotify_service.get_user(user["id"], refresh=True)
10511102
elif service == "apple_music":
10521103
apple_service = AppleService()
10531104
token = apple_service.get_user(user["id"])
1105+
elif service == "soundcloud":
1106+
soundcloud_service = SoundCloudService()
1107+
token = soundcloud_service.get_user(user["id"])
10541108

10551109
if not token:
10561110
raise APIBadRequest(f"Service {service} is not linked. Please link your {service} account first.")
@@ -1067,6 +1121,8 @@ def export_playlist_jspf(service):
10671121
url = export_to_spotify(user["auth_token"], token["access_token"], is_public, jspf=jspf)
10681122
elif service == "apple_music":
10691123
url = export_to_apple_music(user["auth_token"], token["access_token"], token["refresh_token"], is_public, jspf=jspf)
1124+
elif service == "soundcloud":
1125+
url = export_to_soundcloud(user["auth_token"], token["access_token"], is_public, jspf=jspf)
10701126
return jsonify({"external_url": url})
10711127
except requests.exceptions.HTTPError as exc:
10721128
error = exc.response.json()

0 commit comments

Comments
 (0)