diff options
Diffstat (limited to 'breaker.py')
| -rwxr-xr-x | breaker.py | 394 |
1 files changed, 394 insertions, 0 deletions
diff --git a/breaker.py b/breaker.py new file mode 100755 index 0000000..99e711b --- /dev/null +++ b/breaker.py @@ -0,0 +1,394 @@ +#!/usr/bin/python2.7 +from subprocess import call, Popen, PIPE +import urllib +import urllib2 +import os +import sys +import random +import re +import time +urlencode = urllib.urlencode +urlopen = urllib2.urlopen +Request = urllib2.Request + +WORKING_DIR = "/tmp" +BIN_CONVERT = "/usr/bin/convert" +BIN_IDENTIFY = "/usr/bin/identify" +DEFAULT_FINALFORMAT = "png"; + +SUBTLE_BREAK_MARK = 'pron' +EXTREME_BREAK_MARK = 'sugar' + +HEADER_OFFSET = 5000 + +MAX_SIZE = 1024 * 1024 * 1.2 * 1.5 + +# 'CLASSIC':'jpg', +# 'REDUX':'pcds', +# 'BLURRY_BREAK':'viff', +# 'BLURRY_BREAK_2':'mat', +# 'SWIPE':'miff', +# 'RGB_WASH':'psd', +# 'RGB_WASH_2':'psb', +# 'NOISY_BREAK':'palm', +# 'NOISY_BREAK_2':'fig', +# 'BROKEN_VIGNETTE':'pbm', +# 'FAX_MACHINE':'cals', +# 'STRIPES':'exr', +# 'PHOTOCOPY':'art', +TEST_PARAMS = { + "url" : "http://i.asdf.us/im/27/1424816234661dumpfmpfifferkinggr_1424816412_pfifferking.gif" , + "breaktype" : "RGB_WASH", + "finalformat" : "png", + "breakmode" : "extreme", + "breakangle" : "10", + "username" : "donkey", + "expanded" : "false" +} + +def bool_correct(s): + if re.match(r'^false$', s, re.IGNORECASE): + return False + elif re.match(r'^true$', s, re.IGNORECASE): + return True + else: + return s + +#{{{Utility functions +class dotdict(dict): + """dot.notation access to dictionary attributes""" + def __getattr__(self, attr): + return self.get(attr) + __setattr__= dict.__setitem__ + __delattr__= dict.__delitem__ +def sanitize (str): + return re.sub(r'\W+', '', str) + +def now(): + return int(time.time()) + +def browser_request (url, data=None): + headers = { + 'User-Agent': 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)', + 'Accept': '*/*', + } + try: + req = Request(url, data, headers) + response = urlopen(req) + except IOError, e: + if hasattr(e, 'code'): + sys.stderr.write( '%s - ERROR %s' % (url, e.code) ) + raise; + return None + else: + return response + +def download(url, destination, max_size=MAX_SIZE): + response = browser_request(url, None) + rawimg = response.read() + if len(rawimg) == 0: + sys.stderr.write("got zero-length file") + raise; + if len(rawimg) > max_size: + sys.stderr.write("file too big: max size {} KB / {} is {} KB".format( + str(MAX_SIZE/1024), + destination, + str(len(rawimg)/1024) + )) + raise; + f = open(destination, "w") + f.write(rawimg) + f.close() + +def dimensions (filepath): + #works in lieu of a mimetype check (it reads the header as well) + ident = (Popen([BIN_IDENTIFY, filepath], stdout=PIPE).communicate()[0]).split(" ") + return ident[2].split("x") + +def file_size (filepath): + try: + return os.stat(file)[6] + except Exception as e: + sys.stderr.write(str(e)) + raise; + +def gif_frames(filepath): + try: + info = Popen([BIN_IDENTIFY,filepath], stdout=PIPE).communicate()[0] + frames = filter((lambda x: x), map( + (lambda x: x.split(" ")[0]), + (info).split('\n') + )) + return frames + except Exception as e: + sys.stderr.write(str(e)) + raise; +#}}} + +class Breaker(): + def __init__(self, params): + self.params = {} + self.now = now() + self.tag = "imBreak" + self.commands = []; + self.required_keys = [ + "url", + "breaktype", + "finalformat", + "breakmode", + "breakangle", + "username", + "expanded" + ] + self.files_created = [] + for k in self.required_keys: + if k in params: + if k == 'breaktype': + self.params['breaktype'] = self._get_breaktype(params[k]) + elif k == 'url': + self.params[k] = params[k] + else: + self.params[k] = bool_correct(sanitize(params[k])) + else: + self.params[k] = False; + + + self.params = dotdict(self.params) + + self.basename, self.first_format = self._get_filename(); + self.downloaded_file = os.path.join(WORKING_DIR, "IMBREAKTMP{}.{}".format(self.basename, self.first_format)) + + try: + download(self.params.url, self.downloaded_file) + self.files_created.append(self.downloaded_file) + except Exception as e: + sys.stderr.write(str(e)) + raise; + self.gif_frames = gif_frames(self.downloaded_file) + self.gif_frames = self.gif_frames if len(self.gif_frames) > 1 else False + self.width, self.height = dimensions(self.downloaded_file) + + if not self.params.finalformat: + self.params.finalformat = DEFAULT_FINALFORMAT + if self.gif_frames: + self.params.finalformat = 'gif' + if self.params.breaktype == 'miff': + self.params.finalformat = 'jpg' + self.params.breakmode = 'subtle' + #final filepath is stored in self.filepath + self.filename = "{}.{}".format(self.basename, self.params.finalformat) + self.filepath = os.path.join(WORKING_DIR, self.filename) + self.conversion_file = os.path.join(WORKING_DIR, "IMBREAKTMP{}.{}".format(self.basename, self.params.breaktype)) + + def _get_breaktype(self, key): + #{{{ conversion table + breaktypeTranslate = { + 'CLASSIC':'jpg', + 'REDUX':'pcds', + 'BLURRY_BREAK':'viff', + 'BLURRY_BREAK_2':'mat', + 'SWIPE':'miff', + 'RGB_WASH':'psd', + 'RGB_WASH_2':'psb', + 'NOISY_BREAK':'palm', + 'NOISY_BREAK_2':'fig', + 'BROKEN_VIGNETTE':'pbm', + 'FAX_MACHINE':'cals', + 'STRIPES':'exr', + 'PHOTOCOPY':'art', + } + #}}} + return breaktypeTranslate[key] + + def _get_filename (self): + url = self.params.url + name_part = ""; + file_format = ""; + if "?" in url: + url = url.split("?")[0] + if "/" in url: + url = urllib.unquote(url).replace(" ","") + name_part = url.split("/")[-1] + try: + parts = name_part.split(".") + name_part = sanitize(parts[-2]) + file_format = sanitize(parts[-1]) + if not name_part or not file_format: + sys.stderr.write( "Incompatible input file type") + raise; + except Exception as e: + sys.stderr.write( "Incompatible input file type") + raise; + else: + sys.stderr.write( "Incompatible url") + raise; + if (len(name_part) > 20): + name_part = name_part[:-20] + return "{}{}_{}_{}".format(self.tag, name_part, self.now, self.params.username or ""), file_format + +#{{{#########rotatefunctions####################################### + def _rotate(self): + try: + call([BIN_CONVERT,self.downloaded_file,"-rotate",self.params.breakangle,"+repage",self.downloaded_file]) + except Exception as e: + sys.stderr.write(str(e)) + raise; + def _rotate_back(self): + try: + angle = str(360-int(self.params.breakangle)) + call([BIN_CONVERT,self.filepath,"-rotate",angle,"+repage",self.filepath]) + except Exception as e: + sys.stderr.write(str(e)) + raise; + if not self.params.expanded: + try: + call( + [BIN_CONVERT,self.filepath,"-gravity","Center","-crop","{}x{}+0+0".format( + self.width, self.height),"+repage",self.filepath + ]) + except Exception as e: + sys.stderr.write(str(e)) + raise; +#}}} + def _subtle_break(self): + #assume the header is no longer than HEADER_OFFSET bytes + breakpoint = random.randint(HEADER_OFFSET, len(self.file_data)) + newfile = "" + newfile = self.file_data[0:breakpoint]; + newfile += SUBTLE_BREAK_MARK; + newfile += self.file_data[breakpoint:] + self.file_data = newfile[0:len(self.file_data)] + + def _extreme_break(self): + increment = len(self.file_data)/10; + i = 0 + newfile = ""; + for b in self.file_data: + if i > HEADER_OFFSET and not (i % increment): + b += EXTREME_BREAK_MARK + newfile += b + i += 1 + self.file_data = newfile[0:len(self.file_data)] + + def _choose_frame(self): + frame = random.choice(self.gif_frames) + try: + call([BIN_CONVERT, frame, self.downloaded_file]) + except Exception as e: + sys.stderr.write(str(e)) + raise; + + def _enforce_jpg(self): + if self.params.breaktype in [ "exr", "bmp", "miff" ] and not re.match(r'jpe?g', self.first_format, re.IGNORECASE): + jpg_file = os.path.join(WORKING_DIR, "{}.{}".format(self.basename, "jpg")) + try: + call([BIN_CONVERT,self.downloaded_file,jpg_file]) + call(["rm",self.downloaded_file]) + self.downloaded_file = jpg_file + except Exception as e: + sys.stderr.write(str(e)) + raise; + + def _first_conversion(self): + if self.first_format == self.params.breaktype: + self.downloaded_file = self.conversion_file + return + try: + call([BIN_CONVERT, self.downloaded_file, self.conversion_file]); + self.files_created.append(self.conversion_file) + except Exception as e: + sys.stderr.write(str(e)) + raise; + + def _read_data(self, filepath): + f = open(filepath, 'r'); + data = f.read() + f.close() + return data + + def _prepare_filedata(self): + if self.gif_frames: + self._choose_frame() + if self.params.breakangle: + self._rotate() + self._enforce_jpg(); + self._first_conversion(); + self.file_data = self._read_data(self.conversion_file) + if not self.file_data: + sys.stderr.write("Unable to get file_data") + raise; + + def _add_false_data(self, breakmode): + if breakmode == "subtle": + self._subtle_break() + elif breakmode == "extreme": + self._extreme_break() + f = open(self.conversion_file, 'w') + f.write(self.file_data) + f.close(); + +#{{{ SHRINK (UNUSED) + def _shrink(self): + cmd = [ BIN_CONVERT, "-resize", "500x500", self.downloaded_file, self.downloaded_file ]; + try: + call(cmd) + self.commands.append(" ".join(cmd)); + except Exception as e: + sys.stderr.write(str(e)) + raise; +#}}} + + def _final_conversion(self): + cmd = [BIN_CONVERT, self.conversion_file, self.filepath] + try: + call(cmd) + self.commands.append(" ".join(cmd)); + except Exception as e: + sys.stderr.write(str(e)) + raise; + #handle multiple files created by psd and psb + def psd_psbfilepath(num): + return os.path.join(WORKING_DIR, "{}-{}.{}".format(self.basename, num, self.params.finalformat)) + if self.params.breaktype == 'psd': + cmd = ['mv', psd_psbfilepath(1), self.filepath] + try: + call(cmd) + self.commands.append(" ".join(cmd)); + except Exception as e: + sys.stderr.write(str(e)) + raise + self.files_created.append(psd_psbfilepath(0)) + if self.params.breaktype == 'psb': + cmd = ['mv', psd_psbfilepath(0), self.filepath] + try: + call(cmd) + self.commands.append(" ".join(cmd)); + except Exception as e: + sys.stderr.write(str(e)) + raise + self.files_created.append(psd_psbfilepath(1)) + + if self.params.breakangle: + self._rotate_back() + + def _cleanup(self): + cmd = ["rm"]+self.files_created + try: + call(cmd) + self.commands.append(" ".join(cmd)); + except Exception as e: + sys.stderr.write(str(e)) + raise + + def create(self, breakmode=""): + if not breakmode: breakmode = self.params.breakmode + self._prepare_filedata(); + self._add_false_data(breakmode); + self._final_conversion() + self._cleanup() + + +if __name__ == "__main__": + b = Breaker(TEST_PARAMS) + b.create(); + print b.filepath |
