# Soundcloud to CSV File

This program connects to Soundcloud and turns the supplied username into a CSV file with the user's like list. The code accomplishes this although it looks rather messy and was wondering what I could change to improve it.

import soundcloud
import urllib.request
import json
import requests

client = soundcloud.Client(client_id="********************************")

"""
"""
print(user_id)

"""
Checks if supplied username is valid.
"""
global user
try:
user_info(user.id)
except requests.HTTPError:  # Runs if account cannot be found.

def user_info(user_id):
"""
Retrieves the users information from their user ID
and prints the number of tracks they have liked.
"""
global number_of_user_likes
retrieve = urllib.request.urlopen("https://api.soundcloud.com/users/{}.json?consumer_key="
number_of_user_likes = decode_user_info['public_favorites_count']
print("You have liked a total of {} tracks".format(number_of_user_likes))

"""
Creates a CSV file with headers and then saves the users likes to the CSV file.
"""
csv_file = open("{}s like list.csv".format(username), 'w', encoding='UTF-8')
csv_file.write("Track Title, Track URL\n")  # Writes headers to CSV file.
offset_number = 0
complete = False
while number_of_user_likes > 0 and complete is False:
if offset_number < number_of_user_likes:
try:
track_fetch = urllib.request.urlopen(
"http://api.soundcloud.com/users/{}/favorites.json?client_id="
if "," in track_data[0]["title"]:
track_title = track_data[0]["title"].replace(",",
"")  # Removes commas as causes issues with .csv files
else:
track_title = track_data[0]["title"]
offset_number += 1
print("{} of {} ({}%)".format(offset_number, number_of_user_likes,
round(float(100 / number_of_user_likes * offset_number), 2)))
except IndexError:
print("There is an issue with Soundcloud, please try again")
except requests.HTTPError:
print("There is an issue with Soundcloud, please try again")
except requests.ConnectionError:
else:
complete = True
finished()

def finished():
"""
Runs after the users like list has finished being saved.
"""
print("Finished")



## Use of functions

Functions can return values. You should do that instead of passing back results indirectly via global variables.

In get_username(), user_id = (valid_username(username)) is nonsense, since the valid_username() function never returns anything.

Worse, you are misusing functions as if they were goto labels. get_username() and valid_username() are mutually recursive, which is inappropriate. Furthermore, get_username() triggers all of the activity in the program, which is much more than getting the username as the function name would suggest.

Rearranging everything…

def prompt_username():

try:
except requests.HTTPError:

def prompt_account():
while True:
try:
except ValueError:

def get_favorites(account):
req = urllib.request.urlopen("https://api.soundcloud.com/users/{}.json?consumer_key={}",
account.id, consumer_key)
…

def save_favorites(account, favorites):
…


Now you can write this, which does convey what the program does, unlike your get_username() "function" which was really just a goto label:

account = prompt_account()
save_favorites(account, get_favorites(account))

• First of all I would use a class for this, handling variables between functions using globals is very ugly and hard to maintain. Using a class we can easily share attributes across all of its methods easily and cleanly.

• user_id = (valid_username(username)) is actually useless as you're not returning anything from that function.

• To shorten our urls we can create a global variable:

   API_BASE_URL = "https://api.soundcloud.com"

• As you are importing the requests library why not use its .get() method as well instead of urllib.request.urlopen. Another advantage here is that the Response object returned here has a .json() method that returns a dict to us. So, code will simplify to:

    r = requests.get(url)
decode_user_info = r.json()

• For creating a csv file better use the csv module instead of writing lines with , and also you are removing commas from track titles to make your workaround work.

• For handling files always use the with statement, it makes sure the file is closed properly in case an error occurs.

• Instead of using while number_of_user_likes > 0 and complete is False: if offset_number < number_of_user_likes:..., you can use a simple for-loop with range():

    for offset_number in range(self.number_of_likes):


Complete code:

import csv
import soundcloud
import urllib.request
import requests

CLIENT_ID = "*******************************"
CLIENT_SECRET = "******************************"
API_BASE_URL = "https://api.soundcloud.com"

class Client:

def __init__(self, client_id, client_secret):
self.client = soundcloud.Client(client_id=client_id,
client_secret=client_secret)
self.client_id = client_id
self.user_info()
self.save_likes()

"""
"""

"""
Checks if supplied username is valid.
"""
try:
except requests.HTTPError:  # Runs if account cannot be found.

def user_info(self):
"""
Retrieves the users information from their user ID
and prints the number of tracks they have liked.
"""
r = requests.get("{}/users/{}.json?client_id={}".format(API_BASE_URL,
self.user.id,
self.client_id))

decode_user_info = r.json()
self.number_of_likes = decode_user_info['public_favorites_count']
print("You have liked a total of {} tracks".format(self.number_of_likes))

def save_likes(self):
"""
Creates a CSV file with headers and then saves the users likes to the CSV file.
"""
with open("{}s like list.csv".format(self.username), 'w', encoding='UTF-8') as f:
writer = csv.writer(f)
writer.writerow(["Track Title", "Track URL"])  # Writes headers to CSV file.
for offset_number in range(self.number_of_likes):
try:
r = requests.get("{}/users/{}/favorites.json?client_id={}&offset={}&limit=1".format(API_BASE_URL,
self.user.id,
self.client_id,
offset_number))
track_data = r.json()[0]
print("{} of {} ({}%)".format(offset_number+1, self.number_of_likes,
round(float(100 / self.number_of_likes * offset_number), 2)))
except IndexError:
print("There is an issue with Soundcloud, please try again")
except requests.HTTPError:
print("There is an issue with Soundcloud, please try again")
except requests.ConnectionError:
self.finished()

def finished(self):
"""
Runs after the users like list has finished being saved.
"""
print("Finished")

if __name__ == '__main__':
Client(CLIENT_ID, CLIENT_SECRET)


## CSV support

Python has a csv library. Use it. It handles some issues for you automatically, such as the quoting of special characters.

## Ineffective use of the SoundCloud API client

There should be no reason to resort to using the requests library when you're already using an API client that is built for the task. You don't have to worry about HTTP or JSON. It's as simple as this:

import csv
import soundcloud
import requests

client = soundcloud.Client(client_id=CLIENT)

try:
csv_writer = csv.writer(file)
csv_writer.writerow(['Track Title', 'Track URL'])
for fav in favorites: