giuliomoro@229
|
1 # Copyright 2015,2016 Enzien Audio, Ltd. All Rights Reserved.
|
chris@160
|
2
|
chris@160
|
3 import argparse
|
giuliomoro@540
|
4 import datetime
|
chris@160
|
5 import getpass
|
chris@160
|
6 import json
|
chris@160
|
7 import os
|
giuliomoro@549
|
8 import requests # http://docs.python-requests.org/en/master/api/#exceptions
|
chris@160
|
9 import shutil
|
chris@160
|
10 import stat
|
giuliomoro@549
|
11 import sys
|
chris@160
|
12 import tempfile
|
chris@160
|
13 import time
|
chris@160
|
14 import urlparse
|
chris@160
|
15 import zipfile
|
chris@160
|
16
|
chris@160
|
17 class Colours:
|
chris@160
|
18 purple = "\033[95m"
|
chris@160
|
19 cyan = "\033[96m"
|
chris@160
|
20 dark_cyan = "\033[36m"
|
chris@160
|
21 blue = "\033[94m"
|
chris@160
|
22 green = "\033[92m"
|
chris@160
|
23 yellow = "\033[93m"
|
chris@160
|
24 red = "\033[91m"
|
chris@160
|
25 bold = "\033[1m"
|
chris@160
|
26 underline = "\033[4m"
|
chris@160
|
27 end = "\033[0m"
|
chris@160
|
28
|
giuliomoro@549
|
29 class ErrorCodes(object):
|
giuliomoro@549
|
30 # NOTE(mhroth): this class could inherit from Enum, but we choose not to
|
giuliomoro@549
|
31 # as to not require an additional dependency
|
giuliomoro@549
|
32 # http://www.tldp.org/LDP/abs/html/exitcodes.html
|
giuliomoro@549
|
33 # http://stackoverflow.com/questions/1101957/are-there-any-standard-exit-status-codes-in-linux
|
giuliomoro@549
|
34 CODE_OK = 0 # success!
|
giuliomoro@549
|
35 CODE_MAIN_NOT_FOUND = 3 # _main.pd not found
|
giuliomoro@549
|
36 CODE_HEAVY_COMPILE_ERRORS = 4 # heavy returned compiler errors
|
giuliomoro@549
|
37 CODE_UPLOAD_ASSET_TOO_LARGE = 5 # the size of the uploadable asset is too large
|
giuliomoro@549
|
38 CODE_RELEASE_NOT_AVAILABLE = 6 # the requested release is not available
|
giuliomoro@549
|
39 CODE_CONNECTION_ERROR = 7 # HTTPS connection could not be made to the server
|
giuliomoro@549
|
40 CODE_CONNECTION_TIMEOUT = 8 # HTTPS connection has timed out
|
giuliomoro@549
|
41 CODE_CONNECTION_400_500 = 9 # a 400 or 500 error has occured
|
giuliomoro@549
|
42 CODE_EXCEPTION = 125 # a generic execption has occurred
|
giuliomoro@549
|
43
|
giuliomoro@549
|
44 class UploaderException(Exception):
|
giuliomoro@549
|
45 def __init__(self, code, message=None, e=None):
|
giuliomoro@549
|
46 self.code = code
|
giuliomoro@549
|
47 self.message = message
|
giuliomoro@549
|
48 self.e = e
|
giuliomoro@549
|
49
|
giuliomoro@403
|
50 # the maxmimum file upload size of 1MB
|
giuliomoro@549
|
51 __HV_MAX_UPLOAD_SIZE = 1 * 1024*1024
|
giuliomoro@403
|
52
|
chris@160
|
53 def __zip_dir(in_dir, zip_path, file_filter=None):
|
giuliomoro@403
|
54 """Recursively zip an entire directory with an optional file filter
|
giuliomoro@403
|
55 """
|
chris@160
|
56 zf = zipfile.ZipFile(zip_path, mode="w", compression=zipfile.ZIP_DEFLATED)
|
chris@160
|
57 for subdir, dirs, files in os.walk(in_dir):
|
giuliomoro@403
|
58 for f in files:
|
giuliomoro@403
|
59 if (file_filter is None) or (f.lower().split(".")[-1] in file_filter):
|
chris@160
|
60 zf.write(
|
giuliomoro@403
|
61 filename=os.path.join(subdir,f),
|
giuliomoro@403
|
62 arcname=os.path.relpath(os.path.join(subdir,f), start=in_dir))
|
chris@160
|
63 return zip_path
|
chris@160
|
64
|
chris@160
|
65 def __unzip(zip_path, target_dir):
|
chris@160
|
66 """Unzip a file to a given directory. All destination files are overwritten.
|
chris@160
|
67 """
|
chris@160
|
68 zipfile.ZipFile(zip_path).extractall(target_dir)
|
chris@160
|
69
|
chris@160
|
70 def main():
|
chris@160
|
71 parser = argparse.ArgumentParser(
|
chris@160
|
72 description="Compiles a Pure Data file.")
|
chris@160
|
73 parser.add_argument(
|
chris@160
|
74 "input_dir",
|
giuliomoro@229
|
75 help="A directory containing _main.pd. All .pd files in the directory structure will be uploaded.")
|
chris@160
|
76 parser.add_argument(
|
chris@160
|
77 "-n", "--name",
|
chris@160
|
78 default="heavy",
|
giuliomoro@229
|
79 help="Patch name. If it doesn't exist on the Heavy site, the uploader will fail.")
|
chris@160
|
80 parser.add_argument(
|
chris@160
|
81 "-g", "--gen",
|
chris@160
|
82 nargs="+",
|
chris@160
|
83 default=["c"],
|
giuliomoro@229
|
84 help="List of generator outputs. Currently supported generators are "
|
giuliomoro@229
|
85 "'c', 'js', 'pdext', 'pdext-osx', 'unity', 'unity-osx', "
|
giuliomoro@229
|
86 "'unity-win-x86', 'unity-win-x86_64', 'wwise', 'wwise-win-x86_64', "
|
giuliomoro@549
|
87 "'vst2' ,'vst2-osx', 'vst2-win-x86_64', and 'vst2-win-x86'.")
|
chris@160
|
88 parser.add_argument(
|
chris@160
|
89 "-b",
|
giuliomoro@229
|
90 help="All files will be placed in the output directory, placed in their own subdirectory corresponding to the generator name.",
|
giuliomoro@229
|
91 action="count")
|
giuliomoro@229
|
92 parser.add_argument(
|
giuliomoro@229
|
93 "-y",
|
giuliomoro@229
|
94 help="Extract only the generated C files. Static files are deleted. "
|
giuliomoro@229
|
95 "Only effective for the 'c' generator.",
|
chris@160
|
96 action="count")
|
chris@160
|
97 parser.add_argument(
|
chris@160
|
98 "-o", "--out",
|
chris@160
|
99 nargs="+",
|
chris@160
|
100 default=["./"], # by default
|
chris@160
|
101 help="List of destination directories for retrieved files. Order should be the same as for --gen.")
|
chris@160
|
102 parser.add_argument(
|
giuliomoro@403
|
103 "-r", "--release",
|
giuliomoro@403
|
104 help="Optionally request a specific release of Heavy to use while compiling.")
|
giuliomoro@403
|
105 parser.add_argument(
|
chris@160
|
106 "-d", "--domain",
|
chris@160
|
107 default="https://enzienaudio.com",
|
chris@160
|
108 help="Domain. Default is https://enzienaudio.com.")
|
chris@160
|
109 parser.add_argument(
|
chris@160
|
110 "-x",
|
chris@160
|
111 help="Don't save the returned token.",
|
chris@160
|
112 action="count")
|
chris@160
|
113 parser.add_argument(
|
chris@160
|
114 "-z",
|
chris@160
|
115 help="Force the use of a password, regardless of saved token.",
|
chris@160
|
116 action="count")
|
chris@160
|
117 parser.add_argument(
|
chris@160
|
118 "--noverify",
|
giuliomoro@229
|
119 help="Don't verify the SSL connection. This is generally a very bad idea.",
|
chris@160
|
120 action="count")
|
chris@160
|
121 parser.add_argument(
|
chris@160
|
122 "-v", "--verbose",
|
chris@160
|
123 help="Show debugging information.",
|
chris@160
|
124 action="count")
|
giuliomoro@229
|
125 parser.add_argument(
|
giuliomoro@229
|
126 "-t", "--token",
|
giuliomoro@549
|
127 help="Use the specified token.")
|
chris@160
|
128 args = parser.parse_args()
|
chris@160
|
129
|
giuliomoro@549
|
130 try:
|
giuliomoro@549
|
131 # set default values
|
giuliomoro@549
|
132 domain = args.domain or "https://enzienaudio.com"
|
giuliomoro@549
|
133 exit_code = ErrorCodes.CODE_OK
|
giuliomoro@549
|
134 temp_dir = None
|
giuliomoro@549
|
135 post_data = {}
|
chris@160
|
136
|
giuliomoro@549
|
137 # token should be stored in ~/.heavy/token
|
giuliomoro@549
|
138 token_path = os.path.expanduser(os.path.join("~/", ".heavy", "token"))
|
chris@160
|
139
|
giuliomoro@549
|
140 if args.token is not None:
|
giuliomoro@549
|
141 # check if token has been passed as a command line arg...
|
giuliomoro@549
|
142 post_data["credentials"] = {"token": args.token}
|
giuliomoro@549
|
143 elif os.path.exists(token_path) and not args.z:
|
giuliomoro@549
|
144 # ...or if it is stored in the user's home directory
|
giuliomoro@549
|
145 with open(token_path, "r") as f:
|
giuliomoro@549
|
146 post_data["credentials"] = {"token": f.read()}
|
giuliomoro@549
|
147 else:
|
giuliomoro@549
|
148 # otherwise, get the username and password
|
giuliomoro@549
|
149 post_data["credentials"] = {
|
giuliomoro@549
|
150 "username": raw_input("Enter username: "),
|
giuliomoro@549
|
151 "password": getpass.getpass("Enter password: ")
|
giuliomoro@549
|
152 }
|
giuliomoro@229
|
153
|
giuliomoro@549
|
154 tick = time.time()
|
chris@160
|
155
|
giuliomoro@549
|
156 # parse the optional release argument
|
giuliomoro@549
|
157 if args.release:
|
giuliomoro@540
|
158 # check the validity of the current release
|
giuliomoro@540
|
159 releases_json = requests.get(urlparse.urljoin(domain, "/a/releases")).json()
|
giuliomoro@540
|
160 if args.release in releases_json:
|
giuliomoro@540
|
161 today = datetime.datetime.now()
|
giuliomoro@540
|
162 valid_until = datetime.datetime.strptime(releases_json[args.release]["validUntil"], "%Y-%m-%d")
|
giuliomoro@540
|
163 if today > valid_until:
|
giuliomoro@540
|
164 print "{0}Warning:{1} The release \"{2}\" expired on {3}. It may be removed at any time!".format(
|
giuliomoro@540
|
165 Colours.yellow, Colours.end,
|
giuliomoro@540
|
166 args.release,
|
giuliomoro@540
|
167 releases_json[args.release]["validUntil"])
|
giuliomoro@540
|
168 elif (valid_until - today) <= datetime.timedelta(weeks=4):
|
giuliomoro@540
|
169 print "{0}Warning:{1} The release \"{2}\" will expire soon on {3}.".format(
|
giuliomoro@540
|
170 Colours.yellow, Colours.end,
|
giuliomoro@540
|
171 args.release,
|
giuliomoro@540
|
172 releases_json[args.release]["validUntil"])
|
giuliomoro@540
|
173 else:
|
giuliomoro@540
|
174 print "{0}Error:{1} The release \"{2}\" is not available. Available releases are:".format(
|
giuliomoro@540
|
175 Colours.red, Colours.end,
|
giuliomoro@540
|
176 args.release)
|
giuliomoro@540
|
177 for k,v in releases_json.items():
|
giuliomoro@540
|
178 print "* {0} ({1})".format(
|
giuliomoro@540
|
179 k,
|
giuliomoro@540
|
180 v["releaseDate"])
|
giuliomoro@549
|
181 raise UploaderException(ErrorCodes.CODE_RELEASE_NOT_AVAILABLE)
|
giuliomoro@540
|
182
|
giuliomoro@549
|
183 post_data["release"] = args.release
|
giuliomoro@403
|
184
|
giuliomoro@549
|
185 # make a temporary directory
|
giuliomoro@549
|
186 temp_dir = tempfile.mkdtemp(prefix="lroyal-")
|
chris@160
|
187
|
giuliomoro@549
|
188 # zip up the pd directory into the temporary directory
|
chris@160
|
189 if not os.path.exists(os.path.join(args.input_dir, "_main.pd")):
|
giuliomoro@549
|
190 raise UploaderException(
|
giuliomoro@549
|
191 ErrorCodes.CODE_MAIN_NOT_FOUND,
|
giuliomoro@549
|
192 "Root Pd directory does not contain a file named _main.pd.")
|
chris@160
|
193 zip_path = __zip_dir(
|
chris@160
|
194 args.input_dir,
|
chris@160
|
195 os.path.join(temp_dir, "archive.zip"),
|
chris@160
|
196 file_filter={"pd"})
|
giuliomoro@403
|
197 if os.stat(zip_path).st_size > __HV_MAX_UPLOAD_SIZE:
|
giuliomoro@549
|
198 raise UploaderException(
|
giuliomoro@549
|
199 ErrorCodes.CODE_UPLOAD_ASSET_TOO_LARGE,
|
giuliomoro@549
|
200 "The target directory, zipped, is {0} bytes. The maximum upload size of 1MB.".format(
|
giuliomoro@549
|
201 os.stat(zip_path).st_size))
|
giuliomoro@549
|
202
|
giuliomoro@549
|
203 post_data["name"] = args.name
|
giuliomoro@549
|
204
|
giuliomoro@549
|
205 # the outputs to generate (always include c)
|
giuliomoro@549
|
206 __SUPPORTED_GENERATOR_SET = {
|
giuliomoro@549
|
207 "c", "js",
|
giuliomoro@549
|
208 "pdext", "pdext-osx",
|
giuliomoro@549
|
209 "unity", "unity-osx", "unity-win-x86", "unity-win-x86_64",
|
giuliomoro@549
|
210 "wwise", "wwise-win-x86_64",
|
giuliomoro@549
|
211 "vst2", "vst2-osx", "vst2-win-x86_64",
|
giuliomoro@549
|
212 }
|
giuliomoro@549
|
213 post_data["gen"] = list(({"c"} | {s.lower() for s in set(args.gen)}) & __SUPPORTED_GENERATOR_SET)
|
giuliomoro@549
|
214
|
giuliomoro@549
|
215 # upload the job, get the response back
|
giuliomoro@549
|
216 # NOTE(mhroth): multipart-encoded file can only be sent as a flat dictionary,
|
giuliomoro@549
|
217 # but we want to send a json encoded deep dictionary. So we do a bit of a hack.
|
giuliomoro@549
|
218 r = requests.post(
|
giuliomoro@549
|
219 urlparse.urljoin(domain, "/a/heavy"),
|
giuliomoro@549
|
220 data={"json":json.dumps(post_data)},
|
giuliomoro@549
|
221 files={"file": (os.path.basename(zip_path), open(zip_path, "rb"), "application/zip")},
|
giuliomoro@549
|
222 verify=False if args.noverify else True)
|
giuliomoro@549
|
223 r.raise_for_status()
|
giuliomoro@549
|
224
|
giuliomoro@549
|
225 """
|
giuliomoro@549
|
226 {
|
giuliomoro@549
|
227 "data": {
|
giuliomoro@549
|
228 "compileTime": 0.05078411102294922,
|
giuliomoro@549
|
229 "id": "mhroth/asdf/Edp2G",
|
giuliomoro@549
|
230 "slug": "Edp2G",
|
giuliomoro@549
|
231 "index": 3,
|
giuliomoro@549
|
232 "links": {
|
giuliomoro@549
|
233 "files": {
|
giuliomoro@549
|
234 "linkage": [
|
giuliomoro@549
|
235 {
|
giuliomoro@549
|
236 "id": "mhroth/asdf/Edp2G/c",
|
giuliomoro@549
|
237 "type": "file"
|
giuliomoro@549
|
238 }
|
giuliomoro@549
|
239 ],
|
giuliomoro@549
|
240 "self": "https://enzienaudio.com/h/mhroth/asdf/Edp2G/files"
|
giuliomoro@549
|
241 },
|
giuliomoro@549
|
242 "project": {
|
giuliomoro@549
|
243 "linkage": {
|
giuliomoro@549
|
244 "id": "mhroth/asdf",
|
giuliomoro@549
|
245 "type": "project"
|
giuliomoro@549
|
246 },
|
giuliomoro@549
|
247 "self": "https://enzienaudio.com/h/mhroth/asdf"
|
giuliomoro@549
|
248 },
|
giuliomoro@549
|
249 "self": "https://enzienaudio.com/h/mhroth/asdf/Edp2G",
|
giuliomoro@549
|
250 "user": {
|
giuliomoro@549
|
251 "linkage": {
|
giuliomoro@549
|
252 "id": "mhroth",
|
giuliomoro@549
|
253 "type": "user"
|
giuliomoro@549
|
254 },
|
giuliomoro@549
|
255 "self": "https://enzienaudio.com/h/mhroth"
|
giuliomoro@549
|
256 }
|
giuliomoro@549
|
257 },
|
giuliomoro@549
|
258 "type": "job"
|
giuliomoro@549
|
259 },
|
giuliomoro@549
|
260 "included": [
|
giuliomoro@549
|
261 {
|
giuliomoro@549
|
262 "filename": "file.c.zip",
|
giuliomoro@549
|
263 "generator": "c",
|
giuliomoro@549
|
264 "id": "mhroth/asdf/Edp2G/c",
|
giuliomoro@549
|
265 "links": {
|
giuliomoro@549
|
266 "self": "https://enzienaudio.com/h/mhroth/asdf/Edp2G/c/file.c.zip"
|
giuliomoro@549
|
267 },
|
giuliomoro@549
|
268 "mime": "application/zip",
|
giuliomoro@549
|
269 "type": "file"
|
giuliomoro@549
|
270 }
|
giuliomoro@549
|
271 ],
|
giuliomoro@549
|
272 "warnings": [
|
giuliomoro@549
|
273 {"details": "blah blah blah"}
|
giuliomoro@549
|
274 ],
|
giuliomoro@549
|
275 "meta": {
|
giuliomoro@549
|
276 "token": "11AS0qPRmjTUHEMSovPEvzjodnzB1xaz"
|
giuliomoro@549
|
277 }
|
giuliomoro@549
|
278 }
|
giuliomoro@549
|
279 """
|
giuliomoro@549
|
280 # decode the JSON API response
|
giuliomoro@549
|
281 reply_json = r.json()
|
giuliomoro@549
|
282 if args.verbose:
|
giuliomoro@549
|
283 print json.dumps(
|
giuliomoro@549
|
284 reply_json,
|
giuliomoro@549
|
285 sort_keys=True,
|
giuliomoro@549
|
286 indent=2,
|
giuliomoro@549
|
287 separators=(",", ": "))
|
giuliomoro@549
|
288
|
giuliomoro@549
|
289 # update the api token, if present
|
giuliomoro@549
|
290 if "token" in reply_json.get("meta",{}) and not args.x:
|
giuliomoro@549
|
291 if args.token is not None:
|
giuliomoro@549
|
292 if reply_json["meta"]["token"] != args.token:
|
giuliomoro@549
|
293 print "WARNING: Token returned by API is not the same as the "
|
giuliomoro@549
|
294 "token supplied at the command line. (old = %s, new = %s)".format(
|
giuliomoro@549
|
295 args.token,
|
giuliomoro@549
|
296 reply_json["meta"]["token"])
|
giuliomoro@549
|
297 else:
|
giuliomoro@549
|
298 if not os.path.exists(os.path.dirname(token_path)):
|
giuliomoro@549
|
299 # ensure that the .heavy directory exists
|
giuliomoro@549
|
300 os.makedirs(os.path.dirname(token_path))
|
giuliomoro@549
|
301 with open(token_path, "w") as f:
|
giuliomoro@549
|
302 f.write(reply_json["meta"]["token"])
|
giuliomoro@549
|
303 # force rw------- permissions on the file
|
giuliomoro@549
|
304 os.chmod(token_path, stat.S_IRUSR | stat.S_IWUSR)
|
giuliomoro@549
|
305
|
giuliomoro@549
|
306 # print any warnings
|
giuliomoro@549
|
307 for i,x in enumerate(reply_json.get("warnings",[])):
|
giuliomoro@549
|
308 print "{3}) {0}Warning:{1} {2}".format(
|
giuliomoro@549
|
309 Colours.yellow, Colours.end, x["detail"], i+1)
|
giuliomoro@549
|
310
|
giuliomoro@549
|
311 # check for errors
|
giuliomoro@549
|
312 if len(reply_json.get("errors",[])) > 0:
|
giuliomoro@549
|
313 for i,x in enumerate(reply_json["errors"]):
|
giuliomoro@549
|
314 print "{3}) {0}Error:{1} {2}".format(
|
giuliomoro@549
|
315 Colours.red, Colours.end, x["detail"], i+1)
|
giuliomoro@549
|
316 raise UploaderException(ErrorCodes.CODE_HEAVY_COMPILE_ERRORS)
|
giuliomoro@549
|
317
|
giuliomoro@549
|
318 # retrieve all requested files
|
giuliomoro@549
|
319 for i,g in enumerate(args.gen):
|
giuliomoro@549
|
320 file_url = __get_file_url_for_generator(reply_json, g)
|
giuliomoro@549
|
321 if file_url and (len(args.out) > i or args.b):
|
giuliomoro@549
|
322 r = requests.get(
|
giuliomoro@549
|
323 file_url,
|
giuliomoro@549
|
324 cookies={"token": reply_json["meta"]["token"]},
|
giuliomoro@549
|
325 verify=False if args.noverify else True)
|
giuliomoro@549
|
326 r.raise_for_status()
|
giuliomoro@549
|
327
|
giuliomoro@549
|
328 # write the reply to a temporary file
|
giuliomoro@549
|
329 c_zip_path = os.path.join(temp_dir, "archive.{0}.zip".format(g))
|
giuliomoro@549
|
330 with open(c_zip_path, "wb") as f:
|
giuliomoro@549
|
331 f.write(r.content)
|
giuliomoro@549
|
332
|
giuliomoro@549
|
333 # unzip the files to where they belong
|
giuliomoro@549
|
334 if args.b:
|
giuliomoro@549
|
335 target_dir = os.path.join(os.path.abspath(os.path.expanduser(args.out[0])), g)
|
giuliomoro@549
|
336 else:
|
giuliomoro@549
|
337 target_dir = os.path.abspath(os.path.expanduser(args.out[i]))
|
giuliomoro@549
|
338 if not os.path.exists(target_dir):
|
giuliomoro@549
|
339 os.makedirs(target_dir) # ensure that the output directory exists
|
giuliomoro@549
|
340 __unzip(c_zip_path, target_dir)
|
giuliomoro@549
|
341
|
giuliomoro@549
|
342 if g == "c" and args.y:
|
giuliomoro@549
|
343 keep_files = ("_{0}.h".format(args.name), "_{0}.c".format(args.name))
|
giuliomoro@549
|
344 for f in os.listdir(target_dir):
|
giuliomoro@549
|
345 if not f.endswith(keep_files):
|
giuliomoro@549
|
346 os.remove(os.path.join(target_dir, f));
|
giuliomoro@549
|
347
|
giuliomoro@549
|
348 print "{0} files placed in {1}".format(g, target_dir)
|
giuliomoro@549
|
349 else:
|
giuliomoro@549
|
350 print "{0}Warning:{1} {2} files could not be retrieved.".format(
|
giuliomoro@549
|
351 Colours.yellow, Colours.end,
|
giuliomoro@549
|
352 g)
|
giuliomoro@549
|
353
|
giuliomoro@549
|
354 print "Job URL:", reply_json["data"]["links"]["self"]
|
giuliomoro@549
|
355 print "Total request time: {0}ms".format(int(1000.0*(time.time()-tick)))
|
giuliomoro@549
|
356 print "Heavy release:", reply_json.get("meta",{}).get("release", "default")
|
giuliomoro@549
|
357 except UploaderException as e:
|
giuliomoro@549
|
358 exit_code = e.code
|
giuliomoro@549
|
359 if e.message:
|
giuliomoro@549
|
360 print "{0}Error:{1} {2}".format(Colours.red, Colours.end, e.message)
|
giuliomoro@549
|
361 except requests.ConnectionError as e:
|
giuliomoro@549
|
362 print "{0}Error:{1} Could not connect to server. Is the server down? Is the internet down?\n{2}".format(Colours.red, Colours.end, e)
|
giuliomoro@549
|
363 exit_code = ErrorCodes.CODE_CONNECTION_ERROR
|
giuliomoro@549
|
364 except requests.ConnectTimeout as e:
|
giuliomoro@549
|
365 print "{0}Error:{1} Connection to server timed out. The server might be overloaded. Try again later?\n{2}".format(Colours.red, Colours.end, e)
|
giuliomoro@549
|
366 exit_code = ErrorCodes.CODE_CONNECTION_TIMEOUT
|
giuliomoro@549
|
367 except requests.HTTPError as e:
|
giuliomoro@549
|
368 print "{0}Error:{1} An HTTP error has occurred.\n{2}".format(Colours.red, Colours.end, e)
|
giuliomoro@549
|
369 exit_code = ErrorCodes.CODE_CONNECTION_400_500
|
chris@160
|
370 except Exception as e:
|
giuliomoro@549
|
371 exit_code = ErrorCodes.CODE_EXCEPTION
|
giuliomoro@403
|
372 print "{0}Error:{1} {2}".format(Colours.red, Colours.end, e)
|
giuliomoro@549
|
373 print "Getting a weird error? Get the latest uploader at https://enzienaudio.com/static/uploader.py"
|
giuliomoro@549
|
374 finally:
|
giuliomoro@549
|
375 if temp_dir:
|
giuliomoro@549
|
376 shutil.rmtree(temp_dir) # delete the temporary directory no matter what
|
chris@160
|
377
|
giuliomoro@549
|
378 # exit and return the exit code
|
giuliomoro@549
|
379 sys.exit(exit_code)
|
chris@160
|
380
|
chris@160
|
381 def __get_file_url_for_generator(json_api, g):
|
chris@160
|
382 """Returns the file link for a specific generator.
|
chris@160
|
383 Returns None if no link could be found.
|
chris@160
|
384 """
|
chris@160
|
385 for i in json_api["included"]:
|
chris@160
|
386 if g == i["generator"]:
|
chris@160
|
387 return i["links"]["self"]
|
chris@160
|
388 return None # by default, return None
|
chris@160
|
389
|
chris@160
|
390
|
chris@160
|
391
|
chris@160
|
392 if __name__ == "__main__":
|
chris@160
|
393 main()
|