forked from 5quinque/Soundcloud-Downloader
-
Notifications
You must be signed in to change notification settings - Fork 0
/
soundcloud-downloader.py
executable file
·126 lines (107 loc) · 4.04 KB
/
soundcloud-downloader.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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#!/usr/bin/python
# 25/04/13
import urllib, urllib2
import sys
import argparse
import re
import lxml.html
import time
from ID3 import *
class SoundCloudDownload:
def __init__(self, url, verbose, tags, related):
self.url = url
self.verbose = verbose
self.tags = tags
self.related = related
self.musicInfo = {}
self.download_progress = 0
self.getInfo(self.url)
def addID3(self, filename, title, artist):
try:
id3info = ID3(filename)
id3info['TITLE'] = title
id3info['ARTIST'] = artist
print "\nID3 tags added"
except InvalidTagError, err:
print "\nInvalid ID3 tag: {0}".format(err)
return 0
def makeURL(self, src):
regexp = 'http://[^/]*/(\w*)_m.png'
match = re.search(regexp, src)
if match:
songid = match.group(1)
url = "http://media.soundcloud.com/stream/{0}".format(songid)
else:
sys.stderr.write("Stream URL not found for source {0}.\n".format(src))
return url
def downloadSong(self, title, src):
valid_chars = '-_.() abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
url = self.makeURL(src)
match = re.search("(.+)\sby\s(.+)\son\sSoundCloud.*", title)
try:
songTitle = match.group(1)
artist = match.group(2).capitalize()
songTitle = ''.join(c for c in songTitle if c in valid_chars)
artist = ''.join(c for c in artist if c in valid_chars)
except AttributeError:
sys.stderr.write("\nError finding title\n")
songTitle = "Not Found"
artist = "Not Found"
return 1
mp3 = "{0} - {1}.mp3".format(songTitle, artist)
sys.stdout.write("\nDownloading: {0}\n".format(mp3))
filename, headers = urllib.urlretrieve(url=url, filename=mp3, reporthook=self.report)
self.addID3(mp3, songTitle, artist)
def getInfo(self, url):
try:
# Opens the URL, using urllib2 for https support
self.html = lxml.html.parse(urllib2.urlopen(url))
except IOError:
# If the URL can not be read, exit
sys.exit('Error: The URL \'{0}\' is borked'.format(url))
# Retrieve the page's title
pageTitle = self.html.xpath('//title/text()')[0]
musicSrc = self.html.xpath("//img[@class='waveform']/@src")[0]
self.musicInfo[pageTitle] = musicSrc
return 0
def report(self, block_no, block_size, file_size):
self.download_progress += block_size
if int(self.download_progress / 1024 * 8) > 1000:
speed = "{0} Mbps".format(round((self.download_progress / 1024 / 1024 * 8) / (time.time() - self.current_time), 2))
else:
speed = "{0} Kbps".format(round((self.download_progress / 1024 * 8) / (time.time() - self.current_time), 2))
rProgress = round(self.download_progress/1024.00/1024.00, 2)
rFile = round(file_size/1024.00/1024.00, 2)
percent = round(100 * float(self.download_progress)/float(file_size))
sys.stdout.write("\r {3} ({0:.2f}/{1:.2f}MB): {2:.2f}% ".format(rProgress, rFile, percent, speed))
sys.stdout.flush()
def main(url, verbose, tags, related):
down = SoundCloudDownload(url, verbose, tags, related)
if down.related:
down.relatedURLs = down.html.xpath("//div[contains(@class, 'player')]//h3//a/@href")
for i in range(len(down.relatedURLs)):
if not down.relatedURLs[i].startswith('http://soundcloud.com'):
down.relatedURLs[i] = 'http://soundcloud.com{0}'.format(down.relatedURLs[i])
for a in down.relatedURLs:
down.getInfo(a)
for mp3 in down.musicInfo:
down.download_progress = 0
down.current_time = time.time()
down.downloadSong(mp3, down.musicInfo[mp3])
sys.stdout.write("Downloaded in: {0} Seconds\n".format(round(time.time() - down.current_time, 2)))
return 0
if __name__ == "__main__":
# parse arguments
parser = argparse.ArgumentParser()
parser.add_argument("-v", "--verbose", help="increase output verbosity",
action="store_true")
parser.add_argument("-t", "--id3tags", help="add id3 tags",
action="store_true")
parser.add_argument("-r", "--related", help="download related songs",
action="store_true")
parser.add_argument("SOUND_URL", help="Soundcloud URL")
args = parser.parse_args()
verbose = bool(args.verbose)
tags = bool(args.id3tags)
related = bool(args.related)
main(args.SOUND_URL, verbose, tags, related)