diff options
Diffstat (limited to 'cgi/img.py')
-rw-r--r-- | cgi/img.py | 792 |
1 files changed, 409 insertions, 383 deletions
@@ -10,411 +10,437 @@ from settings import Settings from database import * from framework import * -try: # Windows needs stdio set for binary mode. - import msvcrt - msvcrt.setmode (0, os.O_BINARY) # stdin = 0 - msvcrt.setmode (1, os.O_BINARY) # stdout = 1 +try: # Windows needs stdio set for binary mode. + import msvcrt + msvcrt.setmode(0, os.O_BINARY) # stdin = 0 + msvcrt.setmode(1, os.O_BINARY) # stdout = 1 except ImportError: - pass + pass -def processImage(post, data, t, originalname, spoiler=False): - """ - Take all post data from <post>, process uploaded file in <data>, and calculate - file names using datetime <t> - Returns updated <post> with file and thumb values - """ - board = Settings._.BOARD - - used_filetype = None - - # get image information - content_type, width, height, size, extra = getImageInfo(data) - - # check the size is fine - if size > int(board["maxsize"])*1024: - raise UserError, _("File too big. The maximum file size is: %s") % board['maxsize'] - - # check if file is supported - for filetype in board['filetypes']: - if content_type == filetype['mime']: - used_filetype = filetype - break - - if not used_filetype: - raise UserError, _("File type not supported.") - - # check if file is already posted - is_duplicate = checkFileDuplicate(data) - if checkFileDuplicate(data)[0]: - raise UserError, _("This image has already been posted %s.") % ('<a href="' + Settings.BOARDS_URL + board['dir'] + '/res/' + str(is_duplicate[1]) + '.html#' + str(is_duplicate[2]) + '">' + _("here") + '</a>') - - # prepare file names - if used_filetype['preserve_name'] == '1': - file_base = os.path.splitext(originalname)[0] # use original filename - else: - file_base = '%d' % int(t * 1000) # generate timestamp name - file_name = file_base + "." + used_filetype['ext'] - file_thumb_name = file_base + "s.jpg" - - # prepare paths - file_path = Settings.IMAGES_DIR + board["dir"] + "/src/" + file_name - file_thumb_path = Settings.IMAGES_DIR + board["dir"] + "/thumb/" + file_thumb_name - file_mobile_path = Settings.IMAGES_DIR + board["dir"] + "/mobile/" + file_thumb_name - file_cat_path = Settings.IMAGES_DIR + board["dir"] + "/cat/" + file_thumb_name - - # remove EXIF data if necessary for privacy - if content_type == 'image/jpeg': - data = removeExifData(data) - - # write file - f = open(file_path, "wb") - try: - f.write(data) - finally: - f.close() - - # set maximum dimensions - maxsize = int(board['thumb_px']) - - post["file"] = file_name - post["image_width"] = width - post["image_height"] = height - - # Do we need to thumbnail it? - if not used_filetype['image']: - # make thumbnail - file_thumb_width, file_thumb_height = getThumbDimensions(width, height, maxsize) - - if used_filetype['ffmpeg_thumb'] == '1': - # use ffmpeg to make thumbnail - logTime("Generating thumbnail") - - if used_filetype['mime'][:5] == 'video': - retcode = subprocess.call([ - Settings.FFMPEG_PATH, '-strict', '-2', '-ss', '0', '-i', file_path, - '-v', 'quiet', '-an', '-vframes', '1', '-f', 'mjpeg', '-vf', 'scale=%d:%d' % (file_thumb_width, file_thumb_height), - '-threads', '1', file_thumb_path]) - if spoiler: - args = [Settings.CONVERT_PATH, file_thumb_path, "-limit", "thread", "1", "-background", "white", "-flatten", "-resize", "%dx%d" % (file_thumb_width, file_thumb_height), "-blur", "0x12", "-gravity", "center", "-fill", "rgba(0,0,0, .6)", "-draw", "rectangle 0,%d,%d,%d" % ((file_thumb_height/2)-10, file_thumb_width, (file_thumb_height/2)+7), "-fill", "white", "-annotate", "0", "Alerta de spoiler", "-quality", str(Settings.THUMB_QUALITY), file_thumb_path] - retcode = subprocess.call(args) - elif used_filetype['mime'][:5] == 'audio': - # we do an exception and use png for audio waveform thumbnails since they - # 1. are smaller 2. allow for transparency - file_thumb_name = file_thumb_name[:-3] + "png" - file_thumb_path = file_thumb_path[:-3] + "png" - file_mobile_path = file_mobile_path[:-3] + "png" - file_cat_path = file_cat_path[:-3] + "png" - - if int(board['thumb_px']) > 149: - file_thumb_width = board['thumb_px'] - file_thumb_height = float(int(board['thumb_px'])/2) - else: - file_thumb_width = 150 - file_thumb_height = 75 - retcode = subprocess.call([ - Settings.FFMPEG_PATH, '-t', '300', '-i', file_path, - '-filter_complex', 'showwavespic=s=%dx%d:split_channels=1' % (int(file_thumb_width), int(file_thumb_height)), - '-frames:v', '1', '-threads', '1', file_thumb_path]) +def processImage(post, data, t, originalname, spoiler=False): + """ + Take all post data from <post>, process uploaded file in <data>, and calculate + file names using datetime <t> + Returns updated <post> with file and thumb values + """ + board = Settings._.BOARD + + used_filetype = None + + # get image information + content_type, width, height, size, extra = getImageInfo(data) + + # check the size is fine + if size > int(board["maxsize"])*1024: + raise UserError, _("File too big. The maximum file size is: %s") % board['maxsize'] + + # check if file is supported + for filetype in board['filetypes']: + if content_type == filetype['mime']: + used_filetype = filetype + break + + if not used_filetype: + raise UserError, _("File type not supported.") + + # check if file is already posted + is_duplicate = checkFileDuplicate(data) + if checkFileDuplicate(data)[0]: + raise UserError, _("This image has already been posted %s.") % ('<a href="' + Settings.BOARDS_URL + board['dir'] + '/res/' + str(is_duplicate[1]) + '.html#' + str(is_duplicate[2]) + '">' + _("here") + '</a>') + + # prepare file names + if used_filetype['preserve_name'] == '1': + file_base = os.path.splitext(originalname)[0] # use original filename + else: + file_base = '%d' % int(t * 1000) # generate timestamp name + file_name = file_base + "." + used_filetype['ext'] + file_thumb_name = file_base + "s.jpg" + + # prepare paths + file_path = Settings.IMAGES_DIR + board["dir"] + "/src/" + file_name + file_thumb_path = Settings.IMAGES_DIR + \ + board["dir"] + "/thumb/" + file_thumb_name + file_mobile_path = Settings.IMAGES_DIR + \ + board["dir"] + "/mobile/" + file_thumb_name + file_cat_path = Settings.IMAGES_DIR + \ + board["dir"] + "/cat/" + file_thumb_name + + # remove EXIF data if necessary for privacy + if content_type == 'image/jpeg': + data = removeExifData(data) + + # write file + f = open(file_path, "wb") + try: + f.write(data) + finally: + f.close() + + # set maximum dimensions + maxsize = int(board['thumb_px']) + + post["file"] = file_name + post["image_width"] = width + post["image_height"] = height + + # Do we need to thumbnail it? + if not used_filetype['image']: + # make thumbnail + file_thumb_width, file_thumb_height = getThumbDimensions( + width, height, maxsize) + + if used_filetype['ffmpeg_thumb'] == '1': + # use ffmpeg to make thumbnail + logTime("Generating thumbnail") + + if used_filetype['mime'][:5] == 'video': + retcode = subprocess.call([ + Settings.FFMPEG_PATH, '-strict', '-2', '-ss', '0', '-i', file_path, + '-v', 'quiet', '-an', '-vframes', '1', '-f', 'mjpeg', '-vf', 'scale=%d:%d' % ( + file_thumb_width, file_thumb_height), + '-threads', '1', file_thumb_path]) + if spoiler: + args = [Settings.CONVERT_PATH, file_thumb_path, "-limit", "thread", "1", "-background", "white", "-flatten", "-resize", "%dx%d" % (file_thumb_width, file_thumb_height), "-blur", "0x12", "-gravity", "center", "-fill", "rgba(0,0,0, .6)", "-draw", "rectangle 0,%d,%d,%d" % ( + (file_thumb_height/2)-10, file_thumb_width, (file_thumb_height/2)+7), "-fill", "white", "-annotate", "0", "Alerta de spoiler", "-quality", str(Settings.THUMB_QUALITY), file_thumb_path] + retcode = subprocess.call(args) + elif used_filetype['mime'][:5] == 'audio': + # we do an exception and use png for audio waveform thumbnails since they + # 1. are smaller 2. allow for transparency + file_thumb_name = file_thumb_name[:-3] + "png" + file_thumb_path = file_thumb_path[:-3] + "png" + file_mobile_path = file_mobile_path[:-3] + "png" + file_cat_path = file_cat_path[:-3] + "png" + + if int(board['thumb_px']) > 149: + file_thumb_width = board['thumb_px'] + file_thumb_height = float(int(board['thumb_px'])/2) + else: + file_thumb_width = 150 + file_thumb_height = 75 + + retcode = subprocess.call([ + Settings.FFMPEG_PATH, '-t', '300', '-i', file_path, + '-filter_complex', 'showwavespic=s=%dx%d:split_channels=1' % ( + int(file_thumb_width), int(file_thumb_height)), + '-frames:v', '1', '-threads', '1', file_thumb_path]) # elif used_filetype['mime'] == 'application/x-shockwave-flash' or used_filetype['mime'] == 'mime/x-shockwave-flash': # retcode = subprocess.call([ # './ffmpeg', '-i', file_path, '-vcodec', 'mjpeg', '-vframes', '1', '-an', '-f', 'rawvideo', # '-vf', 'scale=%d:%d' % (file_thumb_width, file_thumb_height), '-threads', '1', file_thumb_path]) - if retcode != 0: - os.remove(file_path) - raise UserError, _("Thumbnail creation failure.") + ' ('+str(retcode)+')' - else: - # use imagemagick to make thumbnail - args = [Settings.CONVERT_PATH, file_path, "-limit", "thread", "1", "-background", "white", "-flatten", "-resize", "%dx%d" % (file_thumb_width, file_thumb_height)] - if spoiler: - args += ["-blur", "0x12", "-gravity", "center", "-fill", "rgba(0,0,0, .6)", "-draw", "rectangle 0,%d,%d,%d" % ((file_thumb_height/2)-10, file_thumb_width, (file_thumb_height/2)+7), "-fill", "white", "-annotate", "0", "Alerta de spoiler"] - args += ["-quality", str(Settings.THUMB_QUALITY), file_thumb_path] - - # generate thumbnails - logTime("Generating thumbnail") - retcode = subprocess.call(args) - if retcode != 0: - os.remove(file_path) - raise UserError, _("Thumbnail creation failure.") + ' ('+str(retcode)+')' - - # check if thumbnail was truly created - try: - open(file_thumb_path) - except: - os.remove(file_path) - raise UserError, _("Thumbnail creation failure.") - - # create extra thumbnails (catalog/mobile) - subprocess.call([Settings.CONVERT_PATH, file_thumb_path, "-limit" , "thread", "1", "-resize", "100x100", "-quality", "75", file_mobile_path]) - if not post["parentid"]: - subprocess.call([Settings.CONVERT_PATH, file_thumb_path, "-limit" , "thread", "1", "-resize", "150x150", "-quality", "60", file_cat_path]) - - post["thumb"] = file_thumb_name - post["thumb_width"] = file_thumb_width - post["thumb_height"] = file_thumb_height - else: - # Don't thumbnail and use mime image - if board["board_type"] == '0': - post["thumb"] = used_filetype['image'] - post["thumb_width"] = '120' - post["thumb_height"] = '120' + if retcode != 0: + os.remove(file_path) + raise UserError, _("Thumbnail creation failure.") + ' ('+str(retcode)+')' + else: + # use imagemagick to make thumbnail + args = [Settings.CONVERT_PATH, file_path, "-limit", "thread", "1", "-background", + "white", "-flatten", "-resize", "%dx%d" % (file_thumb_width, file_thumb_height)] + if spoiler: + args += ["-blur", "0x12", "-gravity", "center", "-fill", "rgba(0,0,0, .6)", "-draw", "rectangle 0,%d,%d,%d" % ( + (file_thumb_height/2)-10, file_thumb_width, (file_thumb_height/2)+7), "-fill", "white", "-annotate", "0", "Alerta de spoiler"] + args += ["-quality", str(Settings.THUMB_QUALITY), file_thumb_path] + + # generate thumbnails + logTime("Generating thumbnail") + retcode = subprocess.call(args) + if retcode != 0: + os.remove(file_path) + raise UserError, _("Thumbnail creation failure.") + ' ('+str(retcode)+')' + + # check if thumbnail was truly created + try: + open(file_thumb_path) + except: + os.remove(file_path) + raise UserError, _("Thumbnail creation failure.") + + # create extra thumbnails (catalog/mobile) + subprocess.call([Settings.CONVERT_PATH, file_thumb_path, "-limit", "thread", + "1", "-resize", "100x100", "-quality", "75", file_mobile_path]) + if not post["parentid"]: + subprocess.call([Settings.CONVERT_PATH, file_thumb_path, "-limit", + "thread", "1", "-resize", "150x150", "-quality", "60", file_cat_path]) + + post["thumb"] = file_thumb_name + post["thumb_width"] = file_thumb_width + post["thumb_height"] = file_thumb_height else: - post["thumb"] = used_filetype['image'].split(".")[0] + '_small.png' - post["thumb_width"] = '90' - post["thumb_height"] = '90' - - # calculate size (bytes) - post["file_size"] = len(data) - - # add additional metadata, if any - post["message"] += extraInfo(content_type, file_name, file_path) - - # file md5 - post["file_hex"] = getMD5(data) - - return post + # Don't thumbnail and use mime image + if board["board_type"] == '0': + post["thumb"] = used_filetype['image'] + post["thumb_width"] = '120' + post["thumb_height"] = '120' + else: + post["thumb"] = used_filetype['image'].split(".")[0] + '_small.png' + post["thumb_width"] = '90' + post["thumb_height"] = '90' + + # calculate size (bytes) + post["file_size"] = len(data) + + # add additional metadata, if any + post["message"] += extraInfo(content_type, file_name, file_path) + + # file md5 + post["file_hex"] = getMD5(data) + + return post + def extraInfo(mime, file_name, file_path): - board = Settings._.BOARD - - if mime in ['audio/ogg', 'audio/opus', 'audio/mpeg', 'video/webm', 'video/mp4']: - info = ffprobe_f(file_path) - extra = {} - credit_str = "" - - if mime == 'video/webm': - for s in info['streams']: - if 'width' in s: - stream = s - else: - stream = info['streams'][0] - - extra['codec'] = stream.get('codec_name', '').encode('utf-8') - format = info['format'] - - if 'bit_rate' in format: - extra['codec'] += ' ~%d kbps' % int(int(format['bit_rate']) / 1000) - if 'tags' in format: - extra['title'] = format['tags'].get('TITLE', format['tags'].get('title', '')).encode('utf-8') - extra['artist'] = format['tags'].get('ARTIST', format['tags'].get('artist', '')).encode('utf-8') - if extra['title'] or extra['artist']: - credit_str = ' - '.join((extra['artist'], extra['title'])) + ' ' - if 'tags' in stream: - extra['title'] = stream['tags'].get('TITLE', '').encode('utf-8') - extra['artist'] = stream['tags'].get('ARTIST', '').encode('utf-8') - if extra['title'] or extra['artist']: - credit_str = ' - '.join((extra['artist'], extra['title'])) + ' ' - - return '<hr /><small>%s(%s)</small>' % (credit_str, extra['codec']) - - elif mime in ['audio/mod', 'audio/xm', 'audio/s3m']: - ext = mime.split('/')[1].upper() - url = '/cgi/play/%s/%s' % (board['dir'], file_name) - return '<hr /><small>Módulo tracker (%s) [<a href="%s" target="_blank">Click para escuchar</a>]</small>' % (ext, url) - - return '' - -def getImageInfo(data): - data = str(data) - size = len(data) - height = -1 - width = -1 - extra = {} - content_type = "" - - # handle GIFs - if (size >= 10) and data[:6] in ("GIF87a", "GIF89a"): - # Check to see if content_type is correct - content_type = "image/gif" - w, h = struct.unpack("<HH", data[6:10]) - width = int(w) - height = int(h) - - # See PNG 2. Edition spec (http://www.w3.org/TR/PNG/) - # Bytes 0-7 are below, 4-byte chunk length, then 'IHDR' - # and finally the 4-byte width, height - elif ((size >= 24) and data.startswith("\211PNG\r\n\032\n") - and (data[12:16] == "IHDR")): - content_type = "image/png" - w, h = struct.unpack(">LL", data[16:24]) - width = int(w) - height = int(h) - - # Maybe this is for an older PNG version. - elif (size >= 16) and data.startswith("\211PNG\r\n\032\n"): - # Check to see if we have the right content type - content_type = "image/png" - w, h = struct.unpack(">LL", data[8:16]) - width = int(w) - height = int(h) - - # handle JPEGs - elif (size >= 2) and data.startswith("\377\330"): - content_type = "image/jpeg" - jpeg = StringIO(data) - jpeg.read(2) - b = jpeg.read(1) - try: - while (b and ord(b) != 0xDA): - while (ord(b) != 0xFF): b = jpeg.read - while (ord(b) == 0xFF): b = jpeg.read(1) - if (ord(b) >= 0xC0 and ord(b) <= 0xC3): - jpeg.read(3) - h, w = struct.unpack(">HH", jpeg.read(4)) - break + board = Settings._.BOARD + + if mime in ['audio/ogg', 'audio/opus', 'audio/mpeg', 'video/webm', 'video/mp4']: + info = ffprobe_f(file_path) + extra = {} + credit_str = "" + + if mime == 'video/webm': + for s in info['streams']: + if 'width' in s: + stream = s else: - jpeg.read(int(struct.unpack(">H", jpeg.read(2))[0])-2) + stream = info['streams'][0] + + extra['codec'] = stream.get('codec_name', '').encode('utf-8') + format = info['format'] + + if 'bit_rate' in format: + extra['codec'] += ' ~%d kbps' % int(int(format['bit_rate']) / 1000) + if 'tags' in format: + extra['title'] = format['tags'].get( + 'TITLE', format['tags'].get('title', '')).encode('utf-8') + extra['artist'] = format['tags'].get( + 'ARTIST', format['tags'].get('artist', '')).encode('utf-8') + if extra['title'] or extra['artist']: + credit_str = ' - '.join((extra['artist'], + extra['title'])) + ' ' + if 'tags' in stream: + extra['title'] = stream['tags'].get('TITLE', '').encode('utf-8') + extra['artist'] = stream['tags'].get('ARTIST', '').encode('utf-8') + if extra['title'] or extra['artist']: + credit_str = ' - '.join((extra['artist'], + extra['title'])) + ' ' + + return '<hr /><small>%s(%s)</small>' % (credit_str, extra['codec']) + + elif mime in ['audio/mod', 'audio/xm', 'audio/s3m']: + ext = mime.split('/')[1].upper() + url = '/cgi/play/%s/%s' % (board['dir'], file_name) + return '<hr /><small>Módulo tracker (%s) [<a href="%s" target="_blank">Click para escuchar</a>]</small>' % (ext, url) + + return '' + + +def getImageInfo(data): + data = str(data) + size = len(data) + height = -1 + width = -1 + extra = {} + content_type = "" + + # handle GIFs + if (size >= 10) and data[:6] in ("GIF87a", "GIF89a"): + # Check to see if content_type is correct + content_type = "image/gif" + w, h = struct.unpack("<HH", data[6:10]) + width = int(w) + height = int(h) + + # See PNG 2. Edition spec (http://www.w3.org/TR/PNG/) + # Bytes 0-7 are below, 4-byte chunk length, then 'IHDR' + # and finally the 4-byte width, height + elif ((size >= 24) and data.startswith("\211PNG\r\n\032\n") + and (data[12:16] == "IHDR")): + content_type = "image/png" + w, h = struct.unpack(">LL", data[16:24]) + width = int(w) + height = int(h) + + # Maybe this is for an older PNG version. + elif (size >= 16) and data.startswith("\211PNG\r\n\032\n"): + # Check to see if we have the right content type + content_type = "image/png" + w, h = struct.unpack(">LL", data[8:16]) + width = int(w) + height = int(h) + + # handle JPEGs + elif (size >= 2) and data.startswith("\377\330"): + content_type = "image/jpeg" + jpeg = StringIO(data) + jpeg.read(2) b = jpeg.read(1) - width = int(w) - height = int(h) - except struct.error: - pass - except ValueError: - pass - - # handle WebM - elif (size >= 4) and data.startswith("\x1A\x45\xDF\xA3"): - content_type = "video/webm" - info = ffprobe(data) - - # handle mp4 - elif (size >= 8) and data[4:12] in ["ftypmp42", "ftypisom"]: - content_type = "video/mp4" - - # handle ogg formats (vorbis/opus) - elif (size >= 64) and data[:4] == "OggS": - if data[28:35] == "\x01vorbis": - content_type = "audio/ogg" - elif data[28:36] == "OpusHead": - content_type = "audio/opus" - - # handle MP3 - elif (size >= 64) and (data[:3] == "ID3" or data[:3] == "\xFF\xFB"): - content_type = "audio/mpeg" - - # handle MOD - elif (size >= 64) and data[1080:1084] == "M.K.": - content_type = "audio/mod" - - # handle XM - elif (size >= 64) and data.startswith("Extended Module:"): - content_type = "audio/xm" - - # handle S3M - elif (size >= 64) and data[25:32] == "\x00\x00\x00\x1A\x10\x00\x00": - content_type = "audio/s3m" - - # handle PDF - elif (size >= 4) and data[:7] == "%PDF-1.": - content_type = "application/pdf" - - # handle Shockwave Flash - elif (size >= 3) and data[:3] in ["CWS", "FWS"]: - content_type = "application/x-shockwave-flash" - - # handle torrent - elif (size >= 11) and data[:11] == "d8:announce": - content_type = "application/x-bittorrent" - - # handle PDF - elif (size >= 2) and data[:2] == "PK": - content_type = "application/epub+zip" - - if content_type.startswith("video"): - info = ffprobe(data) - for stream in info['streams']: - if 'width' in stream: - width = stream['width'] - height = stream['height'] - break - - - return content_type, width, height, size, extra + try: + while (b and ord(b) != 0xDA): + while (ord(b) != 0xFF): + b = jpeg.read + while (ord(b) == 0xFF): + b = jpeg.read(1) + if (ord(b) >= 0xC0 and ord(b) <= 0xC3): + jpeg.read(3) + h, w = struct.unpack(">HH", jpeg.read(4)) + break + else: + jpeg.read(int(struct.unpack(">H", jpeg.read(2))[0])-2) + b = jpeg.read(1) + width = int(w) + height = int(h) + except struct.error: + pass + except ValueError: + pass + + # handle WebM + elif (size >= 4) and data.startswith("\x1A\x45\xDF\xA3"): + content_type = "video/webm" + info = ffprobe(data) + + # handle mp4 + elif (size >= 8) and data[4:12] in ["ftypmp42", "ftypisom"]: + content_type = "video/mp4" + + # handle ogg formats (vorbis/opus) + elif (size >= 64) and data[:4] == "OggS": + if data[28:35] == "\x01vorbis": + content_type = "audio/ogg" + elif data[28:36] == "OpusHead": + content_type = "audio/opus" + + # handle MP3 + elif (size >= 64) and (data[:3] == "ID3" or data[:3] == "\xFF\xFB"): + content_type = "audio/mpeg" + + # handle MOD + elif (size >= 64) and data[1080:1084] == "M.K.": + content_type = "audio/mod" + + # handle XM + elif (size >= 64) and data.startswith("Extended Module:"): + content_type = "audio/xm" + + # handle S3M + elif (size >= 64) and data[25:32] == "\x00\x00\x00\x1A\x10\x00\x00": + content_type = "audio/s3m" + + # handle PDF + elif (size >= 4) and data[:7] == "%PDF-1.": + content_type = "application/pdf" + + # handle Shockwave Flash + elif (size >= 3) and data[:3] in ["CWS", "FWS"]: + content_type = "application/x-shockwave-flash" + + # handle torrent + elif (size >= 11) and data[:11] == "d8:announce": + content_type = "application/x-bittorrent" + + # handle PDF + elif (size >= 2) and data[:2] == "PK": + content_type = "application/epub+zip" + + if content_type.startswith("video"): + info = ffprobe(data) + for stream in info['streams']: + if 'width' in stream: + width = stream['width'] + height = stream['height'] + break + + return content_type, width, height, size, extra + def ffprobe(data): - import json - p = subprocess.Popen([Settings.FFPROBE_PATH, '-v', 'quiet', '-print_format', 'json', '-show_format', '-show_streams', '-'], - stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) - - out = p.communicate(input=data)[0] - return json.loads(out) - + import json + p = subprocess.Popen([Settings.FFPROBE_PATH, '-v', 'quiet', '-print_format', 'json', '-show_format', '-show_streams', '-'], + stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) + + out = p.communicate(input=data)[0] + return json.loads(out) + + def ffprobe_f(filename): - import json - - p = subprocess.Popen([Settings.FFPROBE_PATH, '-v', 'quiet', '-print_format', 'json', '-show_format', '-show_streams', filename], - stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) - - out = p.communicate()[0] - return json.loads(out) + import json + + p = subprocess.Popen([Settings.FFPROBE_PATH, '-v', 'quiet', '-print_format', 'json', '-show_format', '-show_streams', filename], + stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) + + out = p.communicate()[0] + return json.loads(out) + def getThumbDimensions(width, height, maxsize): - """ - Calculate dimensions to use for a thumbnail with maximum width/height of - <maxsize>, keeping aspect ratio - """ - wratio = (float(maxsize) / float(width)) - hratio = (float(maxsize) / float(height)) - - if (width <= maxsize) and (height <= maxsize): - return width, height - else: - if (wratio * height) < maxsize: - thumb_height = math.ceil(wratio * height) - thumb_width = maxsize + """ + Calculate dimensions to use for a thumbnail with maximum width/height of + <maxsize>, keeping aspect ratio + """ + wratio = (float(maxsize) / float(width)) + hratio = (float(maxsize) / float(height)) + + if (width <= maxsize) and (height <= maxsize): + return width, height else: - thumb_width = math.ceil(hratio * width) - thumb_height = maxsize - - return int(thumb_width), int(thumb_height) + if (wratio * height) < maxsize: + thumb_height = math.ceil(wratio * height) + thumb_width = maxsize + else: + thumb_width = math.ceil(hratio * width) + thumb_height = maxsize + + return int(thumb_width), int(thumb_height) + def checkFileDuplicate(data): - """ - Check that the file <data> does not already exist in a live post on the - current board by calculating its hex and checking it against the database - """ - board = Settings._.BOARD - - file_hex = getMD5(data) - post = FetchOne("SELECT `id`, `parentid` FROM `posts` WHERE `file_hex` = '%s' AND `boardid` = %s AND IS_DELETED = 0 LIMIT 1" % (file_hex, board['id'])) - if post: - if int(post["parentid"]) != 0: - return True, post["parentid"], post["id"] + """ + Check that the file <data> does not already exist in a live post on the + current board by calculating its hex and checking it against the database + """ + board = Settings._.BOARD + + file_hex = getMD5(data) + post = FetchOne("SELECT `id`, `parentid` FROM `posts` WHERE `file_hex` = '%s' AND `boardid` = %s AND IS_DELETED = 0 LIMIT 1" % ( + file_hex, board['id'])) + if post: + if int(post["parentid"]) != 0: + return True, post["parentid"], post["id"] + else: + return True, post["id"], post["id"] else: - return True, post["id"], post["id"] - else: - return False, 0, 0 + return False, 0, 0 + def getJpegSegments(data): - if data[0:2] != b"\xff\xd8": - raise UserError("Given data isn't JPEG.") - - head = 2 - segments = [b"\xff\xd8"] - while 1: - if data[head: head + 2] == b"\xff\xda": - yield data[head:] - break - else: - length = struct.unpack(">H", data[head + 2: head + 4])[0] - endPoint = head + length + 2 - seg = data[head: endPoint] - yield seg - head = endPoint - - if (head >= len(data)): - raise UserDataError("Wrong JPEG data.") - + if data[0:2] != b"\xff\xd8": + raise UserError("Given data isn't JPEG.") + + head = 2 + segments = [b"\xff\xd8"] + while 1: + if data[head: head + 2] == b"\xff\xda": + yield data[head:] + break + else: + length = struct.unpack(">H", data[head + 2: head + 4])[0] + endPoint = head + length + 2 + seg = data[head: endPoint] + yield seg + head = endPoint + + if (head >= len(data)): + raise UserDataError("Wrong JPEG data.") + + def removeExifData(src_data): - exif = None - - for seg in getJpegSegments(src_data): - if seg[0:2] == b"\xff\xe1" and seg[4:10] == b"Exif\x00\x00": - exif = seg - break - - if exif: - return src_data.replace(exif, b"") - else: - return src_data + exif = None + + for seg in getJpegSegments(src_data): + if seg[0:2] == b"\xff\xe1" and seg[4:10] == b"Exif\x00\x00": + exif = seg + break + + if exif: + return src_data.replace(exif, b"") + else: + return src_data |