-
Notifications
You must be signed in to change notification settings - Fork 1
/
sounddrizzle.py
97 lines (74 loc) · 3.03 KB
/
sounddrizzle.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import logging
import urlparse
import click
import termcolor
import mutagen.id3
import mutagen.mp3
import requests
import soundcloud
client = soundcloud.Client(client_id='<Your CLIENT_ID here>')
log = logging.getLogger('SoundDrizzle')
def _bold(text):
return termcolor.colored(text, attrs=['bold'])
@click.command()
@click.argument('track_url')
@click.argument('destination', required=False)
def pour(track_url, destination=None):
try:
track = resolve(track_url)
destination = destination or filename_for_track(track)
track_details = client.get(track.stream_url, allow_redirects=False)
click.echo('Resolved link to {} by {}'.format(_bold(track.title), _bold(track.user['username'])))
stream_url = track_details.location
download(stream_url, destination)
click.echo('Successfully downloaded track to {}'.format(_bold(destination)))
add_metadata(destination, track)
click.echo('Done! Enjoy listening offline!')
except Exception as e:
click.echo('Problem downloading track: {}'.format(e))
def resolve(track_url):
"""
Resolves the URL to an actual track from the SoundCloud API.
If the track resolves to more than one possible track, it takes the first search result.
:returns: The track dictionary from the SoundCloud API
"""
try:
path = urlparse.urlparse(track_url).path
tracks = client.get('/tracks', q=path)
if tracks:
return tracks[0]
else:
raise ValueError('Track not found for URL {}'.format(track_url))
except Exception as e:
raise ValueError('Error obtaining track by url: {}'.format(str(e)))
def filename_for_track(track):
"""
:return: A safe filename for the given track
"""
artist = track.user['permalink']
title = track.title
return '{}-{}.mp3'.format(artist, title).lower().replace(' ', '_').replace('/', '_')
def download(url, target_file, chunk_size=4096):
"""
Simple requests downloader
"""
r = requests.get(url, stream=True)
with open(target_file, 'w+') as out:
# And this is why I love Armin Ronacher:
with click.progressbar(r.iter_content(chunk_size=chunk_size),
int(r.headers['Content-Length'])/chunk_size,
label='Downloading...') as chunks:
for chunk in chunks:
out.write(chunk)
def add_metadata(track_file, track_data):
"""
Adds artist and title from the track data, and downloads the cover and embeds it in the MP3 tags.
"""
# This needs some exception handling!
# We don't always know what type the cover is!
mp3 = mutagen.mp3.MP3(track_file)
mp3['TPE1'] = mutagen.id3.TPE1(encoding=3, text=track_data.user['username'])
mp3['TIT2'] = mutagen.id3.TIT2(encoding=3, text=track_data.title)
cover_bytes = requests.get(track_data.artwork_url, stream=True).raw.read()
mp3.tags.add(mutagen.id3.APIC(encoding=3, mime='image/jpeg', type=3, desc='Front cover', data=cover_bytes))
mp3.save()