scdl/scdl/scdl.py

362 lines
8.9 KiB
Python
Raw Normal View History

#!/usr/bin/python3
"""scdl allow you to download music from soundcloud
Usage:
2014-10-23 14:40:50 -07:00
scdl.py -l <track_url> [-a | -f | -t | -p][-c][-o <offset>][--hidewarnings][--addtofile]
scdl.py me (-s | -a | -f | -t | -p)[-c][-o <offset>][--hidewarnings][--addtofile]
scdl.py -h | --help
scdl.py --version
Options:
2014-10-23 08:22:58 -07:00
-h --help Show this screen.
--version Show version.
2014-11-04 08:58:51 -08:00
me Use the user profile from the auth_token
2014-10-23 08:22:58 -07:00
-l [url] URL can be track/playlist/user.
-s Download the stream of an user (token needed)
-a Download all track of an user (including repost)
-t Download all upload of an user
-f Download all favorite of an user
-p Download all playlist of an user
2014-10-23 10:53:00 -07:00
-c Continue if a music already exist
-o [offset] Begin with a custom offset.
2014-10-23 08:22:58 -07:00
--hidewarnings Hide Warnings. (use with precaution)
2014-11-04 08:58:51 -08:00
--addtofile Add the artist name to the filename if it isn't in the filename already
"""
from docopt import docopt
import configparser
import warnings
import os
import signal
import sys
import string
2014-10-12 15:16:18 -07:00
import time
2014-10-12 15:16:18 -07:00
import soundcloud
import wget
import urllib.request
import json
2014-11-12 08:00:27 -08:00
from mutagen.mp3 import MP3
from mutagen.id3 import ID3, TIT2, TALB, TPE1, TPE2, COMM, USLT, TCOM, TCON, TDRC, APIC
2014-10-12 15:16:18 -07:00
token = ''
2014-10-23 08:22:58 -07:00
2014-10-23 10:53:00 -07:00
offset=0
2014-10-23 08:22:58 -07:00
filename = ''
2014-10-22 10:29:56 -07:00
scdl_client_id = 'b45b1aa10f1ac2941910a7f0d10f8e28'
client = soundcloud.Client(client_id=scdl_client_id)
2014-10-12 15:16:18 -07:00
def main():
"""
Main function, call parse_url
"""
signal.signal(signal.SIGINT, signal_handler)
print("Soundcloud Downloader")
2014-10-23 10:53:00 -07:00
global offset
2014-10-23 14:40:50 -07:00
# import conf file
get_config()
# Parse argument
arguments = docopt(__doc__, version='0.1')
2014-11-12 08:00:27 -08:00
#print(arguments)
2014-10-23 10:53:00 -07:00
if arguments["<offset>"] is not None:
try:
offset=int(arguments["<offset>"])
except:
print('Offset should be an Integer...')
sys.exit()
if arguments["--hidewarnings"]:
warnings.filterwarnings("ignore")
2014-10-22 10:29:56 -07:00
print("no warnings!")
2014-10-23 08:22:58 -07:00
print('')
if arguments["-l"]:
parse_url(arguments["<track_url>"])
2014-10-23 08:22:58 -07:00
elif arguments["me"]:
if arguments["-a"]:
download_all_user_tracks(who_am_i())
elif arguments["-f"]:
download_user_favorites(who_am_i())
elif arguments["-t"]:
download_user_tracks(who_am_i())
elif arguments["-p"]:
download_user_playlists(who_am_i())
2014-10-22 10:29:56 -07:00
def get_config():
"""
read the path where to store music
"""
global token
config = configparser.ConfigParser()
config.read(os.path.join(os.path.expanduser('~'), '.config/scdl/scdl.cfg'))
2014-10-27 12:26:42 -07:00
try:
token = config['scdl']['auth_token']
path = config['scdl']['path']
except:
print('Are you sure scdl.cfg is in $HOME/.config/scdl/ ?')
sys.exit()
2014-10-23 10:53:00 -07:00
if os.path.exists(path):
os.chdir(path)
else:
print('Invalid path...')
sys.exit()
2014-10-23 08:22:58 -07:00
def get_item(track_url):
"""
Fetches metadata for an track or playlist
"""
try:
item = client.get('/resolve', url=track_url)
except Exception as e:
print("Could not resolve url " + track_url)
sys.exit(0)
return item
def parse_url(track_url):
"""
Detects if the URL is a track or playlists, and parses the track(s) to the track downloader
"""
2014-10-23 10:53:00 -07:00
arguments = docopt(__doc__, version='0.1')
2014-10-23 08:22:58 -07:00
item = get_item(track_url)
if not item:
return
elif item.kind == 'track':
print("Found a track")
download_track(item)
elif item.kind == "playlist":
print("Found a playlist")
download_playlist(item)
elif item.kind == 'user':
print("Found an user profile")
if arguments["-f"]:
download_user_favorites(item)
elif arguments["-t"]:
download_user_tracks(item)
elif arguments["-a"]:
download_all_user_tracks(item)
elif arguments["-p"]:
download_user_playlists(item)
2014-10-23 10:53:00 -07:00
else:
print('Please provide a download type...')
2014-10-23 08:22:58 -07:00
else:
print("Unknown item type")
2014-10-23 07:14:29 -07:00
def who_am_i():
"""
display to who the current token correspond, check if the token is valid
"""
2014-10-23 08:22:58 -07:00
global client
client = soundcloud.Client(access_token=token, client_id=scdl_client_id)
2014-10-23 07:14:29 -07:00
try:
current_user = client.get('/me')
except:
print('Invalid token...')
sys.exit(0)
print('Hello',current_user.username, '!')
2014-10-23 08:22:58 -07:00
print('')
return current_user
2014-10-23 07:14:29 -07:00
2014-10-23 08:22:58 -07:00
def download_all_user_tracks(user):
2014-10-23 07:14:29 -07:00
"""
2014-10-23 08:22:58 -07:00
Find track & repost of the user
2014-10-23 07:14:29 -07:00
"""
2014-10-23 10:53:00 -07:00
global offset
2014-10-23 08:22:58 -07:00
user_id = user.id
url = "https://api.sndcdn.com/e1/users/%s/sounds.json?limit=1&offset=%d&client_id=9dbef61eb005cb526480279a0cc868c4" % (user_id, offset)
response = urllib.request.urlopen(url)
data = response.read()
text = data.decode('utf-8')
json_data = json.loads(text)
while json_data != '[]':
offset += 1
try:
this_url = json_data[0]['track']['uri']
except:
this_url = json_data[0]['playlist']['uri']
print('Track n°%d' % (offset))
parse_url(this_url)
2014-10-23 08:22:58 -07:00
url = "https://api.sndcdn.com/e1/users/%s/sounds.json?limit=1&offset=%d&client_id=9dbef61eb005cb526480279a0cc868c4" % (user_id, offset)
response = urllib.request.urlopen(url)
data = response.read()
text = data.decode('utf-8')
json_data = json.loads(text)
2014-10-22 10:29:56 -07:00
2014-10-23 08:22:58 -07:00
def download_user_tracks(user):
2014-10-23 07:14:29 -07:00
"""
2014-10-23 08:22:58 -07:00
Find track in user upload --> no repost
"""
2014-10-23 10:53:00 -07:00
global offset
count=0
tracks = client.get('/users/' + str(user.id) + '/tracks', limit = 10, offset = offset)
for track in tracks:
for track in tracks:
count +=1
print("")
print('Track n°%d' % (count))
download_track(track)
2014-10-23 08:22:58 -07:00
offset += 10
tracks = client.get('/users/' + str(user.id) + '/tracks', limit = 10, offset = offset)
print('All users track downloaded!')
2014-10-23 08:22:58 -07:00
def download_user_playlists(user):
"""
2014-10-23 08:22:58 -07:00
Find playlists of the user
"""
2014-10-23 10:53:00 -07:00
global offset
count=0
playlists = client.get('/users/' + str(user.id) + '/playlists', limit = 10, offset = offset)
for playlist in playlists:
for playlist in playlists:
count +=1
print("")
print('Playlist n°%d' % (count))
download_playlist(playlist)
2014-10-23 08:22:58 -07:00
offset += 10
playlists = client.get('/users/' + str(user.id) + '/playlists', limit = 10, offset = offset)
print('All users playlists downloaded!')
2014-10-23 07:14:29 -07:00
def download_user_favorites(user):
"""
2014-10-23 08:22:58 -07:00
Find tracks in user favorites
"""
2014-10-23 10:53:00 -07:00
global offset
count=0
favorites = client.get('/users/' + str(user.id) + '/favorites', limit = 10, offset = offset)
for track in favorites:
for track in favorites:
count +=1
print("")
print('Favorite n°%d' % (count))
download_track(track)
offset += 10
favorites = client.get('/users/' + str(user.id) + '/favorites', limit = 10, offset = offset)
print('All users favorites downloaded!')
2014-10-12 15:16:18 -07:00
2014-10-23 08:22:58 -07:00
def download_my_stream():
"""
DONT WORK FOR NOW
Download the stream of the current user
"""
client = soundcloud.Client(access_token=token, client_id=scdl_client_id)
current_user = client.get('/me')
activities = client.get('/me/activities')
print(activities)
2014-10-23 07:14:29 -07:00
def download_playlist(playlist):
"""
Download a playlist
"""
count=0
2014-10-23 07:14:29 -07:00
for track_raw in playlist.tracks:
count +=1
2014-10-23 07:14:29 -07:00
mp3_url = get_item(track_raw["permalink_url"])
print('Track n°%d' % (count))
download_track(mp3_url)
2014-10-23 07:14:29 -07:00
def download_track(track):
"""
Downloads a track
"""
2014-10-23 14:40:50 -07:00
global filename
arguments = docopt(__doc__, version='0.1')
2014-10-23 08:22:58 -07:00
if track.streamable:
stream_url = client.get(track.stream_url, allow_redirects=False)
else:
2014-10-23 14:40:50 -07:00
print('%s is not streamable...' % (track.title))
2014-10-23 08:22:58 -07:00
print('')
return
url = stream_url.location
title = track.title
print("Downloading " + title)
2014-10-23 14:40:50 -07:00
# validate title
invalid_chars = '\/:*?|<>'
if track.user['username'] not in title and arguments["--addtofile"]:
title = track.user['username'] + ' - ' + title
title = ''.join(c for c in title if c not in invalid_chars)
filename = title +'.mp3'
2014-10-12 15:16:18 -07:00
2014-10-23 14:40:50 -07:00
# Download
if not os.path.isfile(filename):
2014-10-22 10:29:56 -07:00
if track.downloadable:
print('Downloading the orginal file.')
url = track.download_url + '?client_id=' + scdl_client_id
wget.download(url, filename)
2014-10-23 14:40:50 -07:00
else:
2014-10-22 10:29:56 -07:00
wget.download(url, filename)
2014-11-12 08:00:27 -08:00
print('')
else:
2014-10-23 14:40:50 -07:00
if arguments["-c"]:
print(title + " already Downloaded")
print('')
return
else:
print('')
print("Music already exists ! (exiting)")
sys.exit(0)
2014-11-05 08:48:56 -08:00
settags(track)
print('')
2014-10-23 08:22:58 -07:00
print(filename + ' Downloaded.')
print('')
2014-10-23 07:14:29 -07:00
def settags(track):
"""
Set the tags to the mp3
"""
print("Settings tags...")
2014-11-14 13:58:19 -08:00
user = client.get('/users/' + str(track.user_id), allow_redirects=False)
2014-11-05 08:48:56 -08:00
2014-11-12 08:00:27 -08:00
artwork_url = track.artwork_url
2014-11-14 13:58:19 -08:00
if artwork_url is None:
artwork_url = user.avatar_url
2014-11-12 08:00:27 -08:00
artwork_url = artwork_url.replace('large', 't500x500')
urllib.request.urlretrieve(artwork_url, '/tmp/scdl.jpg')
2014-11-05 08:48:56 -08:00
2014-11-12 08:00:27 -08:00
tags = MP3(filename)
tags["TIT2"] = TIT2(encoding=3, text=track.title)
tags["TALB"] = TALB(encoding=3, text='Soundcloud')
tags["TPE1"] = TPE1(encoding=3, text=user.username)
tags["TCON"] = TCON(encoding=3, text=track.genre)
2014-11-14 13:58:19 -08:00
if artwork_url is not None:
tags["APIC"] = APIC(
encoding=3,
mime='image/jpeg',
type=3,
desc='Cover',
data=open('/tmp/scdl.jpg', 'rb').read()
)
2014-11-12 08:00:27 -08:00
tags.save()
2014-10-23 07:14:29 -07:00
def signal_handler(signal, frame):
"""
handle keyboardinterrupt
"""
2014-10-23 10:53:00 -07:00
time.sleep(1)
files = os.listdir()
for f in files:
if not os.path.isdir(f) and ".tmp" in f:
os.remove(f)
print('')
print('Good bye!')
sys.exit(0)
2014-10-12 15:16:18 -07:00
if __name__ == "__main__":
main()