comparison scripts/hvresources/uploader.py @ 229:c768ed1055b0 mergingClockSync

Fixed heavy-midi and updated uploader.py
author Giulio Moro <giuliomoro@yahoo.it>
date Sat, 09 Apr 2016 00:52:57 +0100
parents 5bcf04234f80
children df710a88e355 83e1acf38d35
comparison
equal deleted inserted replaced
228:a0a7f00cf98d 229:c768ed1055b0
1 #!/usr/bin/python 1 # Copyright 2015,2016 Enzien Audio, Ltd. All Rights Reserved.
2
3 # Copyright 2015 Section6. All Rights Reserved.
4 2
5 import argparse 3 import argparse
6 import getpass 4 import getpass
7 import json 5 import json
8 import os 6 import os
11 import stat 9 import stat
12 import tempfile 10 import tempfile
13 import time 11 import time
14 import urlparse 12 import urlparse
15 import zipfile 13 import zipfile
16 import sys
17 14
18 class Colours: 15 class Colours:
19 purple = "\033[95m" 16 purple = "\033[95m"
20 cyan = "\033[96m" 17 cyan = "\033[96m"
21 dark_cyan = "\033[36m" 18 dark_cyan = "\033[36m"
45 def main(): 42 def main():
46 parser = argparse.ArgumentParser( 43 parser = argparse.ArgumentParser(
47 description="Compiles a Pure Data file.") 44 description="Compiles a Pure Data file.")
48 parser.add_argument( 45 parser.add_argument(
49 "input_dir", 46 "input_dir",
50 help="A directory containing _main.pd. The entire directory will be uploaded.") 47 help="A directory containing _main.pd. All .pd files in the directory structure will be uploaded.")
51 parser.add_argument( 48 parser.add_argument(
52 "-n", "--name", 49 "-n", "--name",
53 default="heavy", 50 default="heavy",
54 help="Patch name. If it doesn't exist, the uploader will fail. Make sure that it exists on the Heavy website.") 51 help="Patch name. If it doesn't exist on the Heavy site, the uploader will fail.")
55 parser.add_argument( 52 parser.add_argument(
56 "-g", "--gen", 53 "-g", "--gen",
57 nargs="+", 54 nargs="+",
58 default=["c"], 55 default=["c"],
59 help="List of generator outputs. Currently supported generators are 'c' and 'js'.") 56 help="List of generator outputs. Currently supported generators are "
57 "'c', 'js', 'pdext', 'pdext-osx', 'unity', 'unity-osx', "
58 "'unity-win-x86', 'unity-win-x86_64', 'wwise', 'wwise-win-x86_64', "
59 "'vst2' ,'vst2-osx', and 'vst2-win-x86_64'.")
60 parser.add_argument( 60 parser.add_argument(
61 "-b", 61 "-b",
62 help="All files will be placed in the output directory, placed in their own subdirectory corresonding to the generator name.", 62 help="All files will be placed in the output directory, placed in their own subdirectory corresponding to the generator name.",
63 action="count")
64 parser.add_argument(
65 "-y",
66 help="Extract only the generated C files. Static files are deleted. "
67 "Only effective for the 'c' generator.",
63 action="count") 68 action="count")
64 parser.add_argument( 69 parser.add_argument(
65 "-o", "--out", 70 "-o", "--out",
66 nargs="+", 71 nargs="+",
67 default=["./"], # by default 72 default=["./"], # by default
78 "-z", 83 "-z",
79 help="Force the use of a password, regardless of saved token.", 84 help="Force the use of a password, regardless of saved token.",
80 action="count") 85 action="count")
81 parser.add_argument( 86 parser.add_argument(
82 "--noverify", 87 "--noverify",
83 help="Don't verify the SSL connection. Generally a bad idea.", 88 help="Don't verify the SSL connection. This is generally a very bad idea.",
84 action="count") 89 action="count")
85 parser.add_argument( 90 parser.add_argument(
86 "-v", "--verbose", 91 "-v", "--verbose",
87 help="Show debugging information.", 92 help="Show debugging information.",
88 action="count") 93 action="count")
94 parser.add_argument(
95 "-t", "--token",
96 help="Use the specified token.",
97 )
89 args = parser.parse_args() 98 args = parser.parse_args()
90 99
91 domain = args.domain or "https://enzienaudio.com" 100 domain = args.domain or "https://enzienaudio.com"
92 101
93 post_data = {} 102 post_data = {}
94 103
95 # token should be stored in ~/.heavy/token 104 # token should be stored in ~/.heavy/token
96 token_path = os.path.expanduser(os.path.join("~/", ".heavy", "token")) 105 token_path = os.path.expanduser(os.path.join("~/", ".heavy", "token"))
97 if os.path.exists(token_path) and not args.z: 106
107 if args.token is not None:
108 # check if token has been passed as a command line arg...
109 post_data["credentials"] = {"token": args.token}
110 elif os.path.exists(token_path) and not args.z:
111 # ...or if it is stored in the user's home directory
98 with open(token_path, "r") as f: 112 with open(token_path, "r") as f:
99 post_data["credentials"] = { 113 post_data["credentials"] = {"token": f.read()}
100 "token": f.read()
101 }
102 else: 114 else:
103 # otherwise, get the username and password 115 # otherwise, get the username and password
104 post_data["credentials"] = { 116 post_data["credentials"] = {
105 "username": raw_input("Enter username: "), 117 "username": raw_input("Enter username: "),
106 "password": getpass.getpass("Enter password: ") 118 "password": getpass.getpass("Enter password: ")
125 return 137 return
126 138
127 post_data["name"] = args.name 139 post_data["name"] = args.name
128 140
129 # the outputs to generate (always include c) 141 # the outputs to generate (always include c)
130 __SUPPORTED_GENERATOR_SET = {"c", "js"} 142 __SUPPORTED_GENERATOR_SET = {
143 "c", "js",
144 "pdext", "pdext-osx",
145 "unity", "unity-osx", "unity-win-x86", "unity-win-x86_64",
146 "wwise", "wwise-win-x86_64",
147 "vst2", "vst2-osx", "vst2-win-x86_64",
148 }
131 post_data["gen"] = list(({"c"} | set(args.gen)) & __SUPPORTED_GENERATOR_SET) 149 post_data["gen"] = list(({"c"} | set(args.gen)) & __SUPPORTED_GENERATOR_SET)
132 150
133 # upload the job, get the response back 151 # upload the job, get the response back
134 # NOTE(mhroth): multipart-encoded file can only be sent as a flat dictionary, 152 # NOTE(mhroth): multipart-encoded file can only be sent as a flat dictionary,
135 # but we want to send a json encoded deep dictionary. So we do a bit of a hack. 153 # but we want to send a json encoded deep dictionary. So we do a bit of a hack.
139 files={"file": (os.path.basename(zip_path), open(zip_path, "rb"), "application/zip")}, 157 files={"file": (os.path.basename(zip_path), open(zip_path, "rb"), "application/zip")},
140 verify=False if args.noverify else True) 158 verify=False if args.noverify else True)
141 159
142 if r.status_code != requests.codes.ok: 160 if r.status_code != requests.codes.ok:
143 shutil.rmtree(temp_dir) # clean up the temporary directory 161 shutil.rmtree(temp_dir) # clean up the temporary directory
162 print "Getting a weird error? Get the latest uploader at https://enzienaudio.com/static/uploader.py"
144 r.raise_for_status() # raise an exception 163 r.raise_for_status() # raise an exception
145 164
146 # decode the JSON API response 165 # decode the JSON API response
147 r_json = r.json() 166 r_json = r.json()
148 167
191 }, 210 },
192 "mime": "application/zip", 211 "mime": "application/zip",
193 "type": "file" 212 "type": "file"
194 } 213 }
195 ], 214 ],
196 "warnings": [], 215 "warnings": [
216 {"details": "blah blah blah"}
217 ],
197 "meta": { 218 "meta": {
198 "token": "11AS0qPRmjTUHEMSovPEvzjodnzB1xaz" 219 "token": "11AS0qPRmjTUHEMSovPEvzjodnzB1xaz"
199 } 220 }
200 } 221 }
201 """ 222 """
207 indent=2, 228 indent=2,
208 separators=(",", ": ")) 229 separators=(",", ": "))
209 230
210 # update the api token, if present 231 # update the api token, if present
211 if "token" in reply_json.get("meta",{}) and not args.x: 232 if "token" in reply_json.get("meta",{}) and not args.x:
212 if not os.path.exists(os.path.dirname(token_path)): 233 if args.token is not None:
213 os.makedirs(os.path.dirname(token_path)) # ensure that the .heavy directory exists 234 if reply_json["meta"]["token"] != args.token:
214 with open(token_path, "w") as f: 235 print "WARNING: Token returned by API is not the same as the "
215 f.write(reply_json["meta"]["token"]) 236 "token supplied at the command line. (old = %s, new = %s)".format(
216 os.chmod(token_path, stat.S_IRUSR | stat.S_IWUSR) # force rw------- permissions on the file 237 args.token,
238 reply_json["meta"]["token"])
239 else:
240 if not os.path.exists(os.path.dirname(token_path)):
241 # ensure that the .heavy directory exists
242 os.makedirs(os.path.dirname(token_path))
243 with open(token_path, "w") as f:
244 f.write(reply_json["meta"]["token"])
245 # force rw------- permissions on the file
246 os.chmod(token_path, stat.S_IRUSR | stat.S_IWUSR)
217 247
218 # print any warnings 248 # print any warnings
219 for x in r_json["warnings"]: 249 for i,x in enumerate(r_json.get("warnings",[])):
220 print "{0}Warning:{1} {2}".format(Colours.yellow, Colours.end, x["detail"]) 250 print "{3}) {0}Warning:{1} {2}".format(
251 Colours.yellow, Colours.end, x["detail"], i+1)
221 252
222 # check for errors 253 # check for errors
223 if len(r_json.get("errors",[])) > 0: 254 if len(r_json.get("errors",[])) > 0:
224 shutil.rmtree(temp_dir) # clean up the temporary directory 255 shutil.rmtree(temp_dir) # clean up the temporary directory
225 for x in r_json["errors"]: 256 for i,x in enumerate(r_json["errors"]):
226 print "{0}Error:{1} {2}".format(Colours.red, Colours.end, x["detail"]) 257 print "{3}) {0}Error:{1} {2}".format(
227 sys.exit(1) 258 Colours.red, Colours.end, x["detail"], i+1)
228 return 259 return
229 260
230 # retrieve all requested files 261 # retrieve all requested files
231 for i,g in enumerate(args.gen): 262 for i,g in enumerate(args.gen):
232 file_url = __get_file_url_for_generator(reply_json, g) 263 file_url = __get_file_url_for_generator(reply_json, g)
249 target_dir = os.path.abspath(os.path.expanduser(args.out[i])) 280 target_dir = os.path.abspath(os.path.expanduser(args.out[i]))
250 if not os.path.exists(target_dir): 281 if not os.path.exists(target_dir):
251 os.makedirs(target_dir) # ensure that the output directory exists 282 os.makedirs(target_dir) # ensure that the output directory exists
252 __unzip(c_zip_path, target_dir) 283 __unzip(c_zip_path, target_dir)
253 284
285 if g == "c" and args.y:
286 keep_files = ("_{0}.h".format(args.name), "_{0}.c".format(args.name))
287 for f in os.listdir(target_dir):
288 if not f.endswith(keep_files):
289 os.remove(os.path.join(target_dir, f));
290
254 print "{0} files placed in {1}".format(g, target_dir) 291 print "{0} files placed in {1}".format(g, target_dir)
255 else: 292 else:
256 print "{0}Warning:{1} {2} files could not be retrieved.".format( 293 print "{0}Warning:{1} {2} files could not be retrieved.".format(
257 Colours.yellow, Colours.end, 294 Colours.yellow, Colours.end,
258 g) 295 g)
259 296
260 # delete the temporary directory 297 # delete the temporary directory
261 shutil.rmtree(temp_dir) 298 shutil.rmtree(temp_dir)
262 299
263 print "Job URL", reply_json["data"]["links"]["self"] 300 print "Job URL:", reply_json["data"]["links"]["self"]
264 print "Total request time: {0}ms".format(int(1000.0*(time.time()-tick))) 301 print "Total request time: {0}ms".format(int(1000.0*(time.time()-tick)))
265 302 print "Heavy version:", reply_json["meta"]["version"]
266 sys.exit(0)
267 303
268 def __get_file_url_for_generator(json_api, g): 304 def __get_file_url_for_generator(json_api, g):
269 """Returns the file link for a specific generator. 305 """Returns the file link for a specific generator.
270 Returns None if no link could be found. 306 Returns None if no link could be found.
271 """ 307 """