aboutsummaryrefslogtreecommitdiff
path: root/cgi/img.py
diff options
context:
space:
mode:
Diffstat (limited to 'cgi/img.py')
-rw-r--r--cgi/img.py792
1 files changed, 409 insertions, 383 deletions
diff --git a/cgi/img.py b/cgi/img.py
index 3561672..ef64ac0 100644
--- a/cgi/img.py
+++ b/cgi/img.py
@@ -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