12
12
import listenbrainz .db .user as db_user
13
13
from listenbrainz .domain .spotify import SpotifyService , SPOTIFY_PLAYLIST_PERMISSIONS
14
14
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
17
18
from listenbrainz .webserver import db_conn , ts_conn
18
19
from listenbrainz .metadata_cache .apple .client import Apple
19
-
20
+ from listenbrainz . metadata_cache . soundcloud . client import SoundCloud
20
21
from listenbrainz .webserver .utils import parse_boolean_arg
21
22
from listenbrainz .webserver .decorators import crossdomain , api_listenstore_needed
22
23
from listenbrainz .webserver .errors import APIBadRequest , APIInternalServerError , APINotFound , APIForbidden , APIError , PlaylistAPIXMLError , APIUnauthorized
@@ -869,15 +870,18 @@ def export_playlist(playlist_mbid, service):
869
870
if not is_valid_uuid (playlist_mbid ):
870
871
log_raise_400 ("Provided playlist ID is invalid." )
871
872
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 '." )
874
875
875
876
if service == "spotify" :
876
877
spotify_service = SpotifyService ()
877
878
token = spotify_service .get_user (user ["id" ], refresh = True )
878
879
elif service == "apple_music" :
879
880
apple_service = AppleService ()
880
881
token = apple_service .get_user (user ["id" ])
882
+ elif service == "soundcloud" :
883
+ soundcloud_service = SoundCloudService ()
884
+ token = soundcloud_service .get_user (user ["id" ])
881
885
882
886
if not token :
883
887
raise APIBadRequest (f"Service { service } is not linked. Please link your { service } account first." )
@@ -893,6 +897,8 @@ def export_playlist(playlist_mbid, service):
893
897
url = export_to_spotify (user ["auth_token" ], token ["access_token" ], is_public , playlist_mbid = playlist_mbid )
894
898
elif service == "apple_music" :
895
899
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 )
896
902
return jsonify ({"external_url" : url })
897
903
except requests .exceptions .HTTPError as exc :
898
904
error = exc .response .json ()
@@ -917,22 +923,32 @@ def import_playlist_from_music_service(service):
917
923
:resheader Content-Type: *application/json*
918
924
"""
919
925
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'." )
920
935
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 ]()
923
937
924
938
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 )
927
940
elif service == "apple_music" :
928
- apple_service = AppleService ()
929
941
# TODO: implement refresh token for AppleMusic
942
+ apple_service = AppleService ()
930
943
token = apple_service .get_user (user ["id" ])
944
+ elif service == "soundcloud" :
945
+ soundcloud_service = SoundCloudService ()
946
+ token = soundcloud_service .get_user (user ["id" ])
931
947
932
948
if not token :
933
949
raise APIBadRequest (f"Service { service } is not linked. Please link your { service } account first." )
934
950
935
- if service == "apple_music" and not token [ "refresh_token" ] :
951
+ if service == "apple_music" and not token . get ( "refresh_token" ) :
936
952
raise APIBadRequest ("Not authorized to Apple Music. Please link your account first." )
937
953
938
954
if service == "spotify" and not SPOTIFY_PLAYLIST_PERMISSIONS .issubset (set (token ["scopes" ])):
@@ -942,12 +958,17 @@ def import_playlist_from_music_service(service):
942
958
943
959
try :
944
960
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 ()
947
963
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 )
951
972
except requests .exceptions .HTTPError as exc :
952
973
error = exc .response .json ()
953
974
raise APIError (error .get ("error" ) or exc .response .reason , exc .response .status_code )
@@ -960,7 +981,7 @@ def import_playlist_from_music_service(service):
960
981
def import_tracks_from_spotify_playlist (playlist_id ):
961
982
"""
962
983
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.
964
985
965
986
:reqheader Authorization: Token <user token>
966
987
:param playlist_id: The Spotify playlist id to get the tracks from
@@ -996,7 +1017,7 @@ def import_tracks_from_spotify_playlist(playlist_id):
996
1017
def import_tracks_from_apple_playlist (playlist_id ):
997
1018
"""
998
1019
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.
1000
1021
1001
1022
:reqheader Authorization: Token <user token>
1002
1023
: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):
1025
1046
raise APIError (error .get ("error" ) or exc .response .reason , exc .response .status_code )
1026
1047
1027
1048
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
+
1028
1079
@playlist_api_bp .post ("/export-jspf/<service>" )
1029
1080
@crossdomain
1030
1081
@ratelimit ()
@@ -1042,15 +1093,18 @@ def export_playlist_jspf(service):
1042
1093
"""
1043
1094
user = validate_auth_header ()
1044
1095
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 '." )
1047
1098
1048
1099
if service == "spotify" :
1049
1100
spotify_service = SpotifyService ()
1050
1101
token = spotify_service .get_user (user ["id" ], refresh = True )
1051
1102
elif service == "apple_music" :
1052
1103
apple_service = AppleService ()
1053
1104
token = apple_service .get_user (user ["id" ])
1105
+ elif service == "soundcloud" :
1106
+ soundcloud_service = SoundCloudService ()
1107
+ token = soundcloud_service .get_user (user ["id" ])
1054
1108
1055
1109
if not token :
1056
1110
raise APIBadRequest (f"Service { service } is not linked. Please link your { service } account first." )
@@ -1067,6 +1121,8 @@ def export_playlist_jspf(service):
1067
1121
url = export_to_spotify (user ["auth_token" ], token ["access_token" ], is_public , jspf = jspf )
1068
1122
elif service == "apple_music" :
1069
1123
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 )
1070
1126
return jsonify ({"external_url" : url })
1071
1127
except requests .exceptions .HTTPError as exc :
1072
1128
error = exc .response .json ()
0 commit comments