summaryrefslogtreecommitdiff
path: root/breaker.py
diff options
context:
space:
mode:
Diffstat (limited to 'breaker.py')
-rwxr-xr-xbreaker.py394
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