aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cgi/framework.py43
-rw-r--r--cgi/img.py133
-rw-r--r--cgi/manage.py11
-rw-r--r--cgi/post.py9
-rwxr-xr-xcgi/weabot.py21
5 files changed, 97 insertions, 120 deletions
diff --git a/cgi/framework.py b/cgi/framework.py
index 5f95303..8c39f81 100644
--- a/cgi/framework.py
+++ b/cgi/framework.py
@@ -9,6 +9,7 @@ import socket
import _mysql
import urllib
import re
+import logging
from Cookie import SimpleCookie
from settings import Settings
@@ -22,7 +23,7 @@ def setBoard(dir):
"""
if not dir:
raise UserError, _("The specified board is invalid.")
- logTime("Seteando el board " + dir)
+ logging.debug("Seteando el board " + dir)
board = FetchOne(
"SELECT * FROM `boards` WHERE `dir` = '%s' LIMIT 1" % _mysql.escape_string(dir))
if not board:
@@ -32,7 +33,6 @@ def setBoard(dir):
"SELECT * FROM `boards_filetypes` INNER JOIN `filetypes` ON filetypes.id = boards_filetypes.filetypeid WHERE `boardid` = %s ORDER BY `ext` ASC" % _mysql.escape_string(board['id']))
board["filetypes_ext"] = [filetype['ext']
for filetype in board['filetypes']]
- logTime("Board seteado.")
Settings._.BOARD = board
@@ -474,42 +474,3 @@ def send_mail(subject, srcmsg):
s.sendmail(me, [you], msg.as_string())
s.quit()
-
-class weabotLogger:
- def __init__(self):
- self.times = []
-
- def log(self, message):
- self.times.append([time.time(), message])
-
- def allTimes(self):
- output = "Time Logged action\n--------------------------\n"
- start = self.times[0][0]
- for time in self.times:
- difference = str(time[0] - start)
- difference_split = difference.split(".")
- if len(difference_split[0]) < 2:
- difference_split[0] = "0" + difference_split[0]
-
- if len(difference_split[1]) < 7:
- difference_split[1] = (
- "0" * (7 - len(difference_split[1]))) + difference_split[1]
- elif len(difference_split[1]) > 7:
- difference_split[1] = difference_split[1][:7]
-
- output += ".".join(difference_split) + " " + time[1] + "\n"
-
- return output
-
-
-logger = weabotLogger()
-
-
-def logTime(message):
- global logger
- logger.log(message)
-
-
-def logTimes():
- global logger
- return logger.allTimes()
diff --git a/cgi/img.py b/cgi/img.py
index ef64ac0..1c57e37 100644
--- a/cgi/img.py
+++ b/cgi/img.py
@@ -4,6 +4,7 @@ import math
#import random
import os
import subprocess
+import logging
from StringIO import StringIO
from settings import Settings
@@ -30,6 +31,7 @@ def processImage(post, data, t, originalname, spoiler=False):
# get image information
content_type, width, height, size, extra = getImageInfo(data)
+ logging.info("{} {} {}".format(content_type, width, height))
# check the size is fine
if size > int(board["maxsize"])*1024:
@@ -87,66 +89,63 @@ def processImage(post, data, t, originalname, spoiler=False):
# Do we need to thumbnail it?
if not used_filetype['image']:
# make thumbnail
+ logging.debug("Generating 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' % (
+
+ try:
+ if used_filetype['ffmpeg_thumb'] == '1':
+ # use ffmpeg to make thumbnail
+ if used_filetype['mime'][:5] == 'video':
+ # Create preview for video AND spoiler it if necessary
+ call_wrap([
+ 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:
+ call_wrap([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", "-font", "Liberation-Sans", "-annotate", "0", "Alerta de spoiler",
+ "-quality", str(Settings.THUMB_QUALITY), file_thumb_path])
+ 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
+
+ call_wrap([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)+')'
+ '-frames:v', '1', '-threads', '1', file_thumb_path])
+ 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",
+ "-font", "Liberation-Sans", "-annotate", "0", "Alerta de spoiler"]
+ args += ["-quality", str(Settings.THUMB_QUALITY), file_thumb_path]
+
+ # generate thumbnails
+ call_wrap(args)
+ except subprocess.CalledProcessError, e:
+ os.remove(file_path)
+ logging.error("Thumbnail creation failure: " + e.output)
+ raise UserError, _("Thumbnail creation failure.") + ' ('+str(e.returncode)+')'
# check if thumbnail was truly created
try:
@@ -293,6 +292,22 @@ def getImageInfo(data):
except ValueError:
pass
+ # handle WebP
+ if data[:4] == b'RIFF' and data[8:12] == b'WEBP':
+ chunk = data[12:]
+ if chunk[:4] == b"VP8 " and chunk[11:14] == b"\x9d\x01\x2a":
+ # Lossy VP8
+ w, h = struct.unpack("HH", chunk[14:18])
+ width = w & 0x3fff
+ height = h & 0x3fff
+ content_type = "image/webp"
+ elif chunk[:4] == b"VP8L":
+ # Lossless VP8
+ b0, b1, b2, b3 = struct.unpack("BBBB", chunk[9:13])
+ width = 1 + (((b1 & 0x3F) << 8) | b0)
+ height = 1 + (((b3 & 0xF) << 10) | (b2 << 2) | ((b1 & 0xC0) >> 6))
+ content_type = "image/webp"
+
# handle WebM
elif (size >= 4) and data.startswith("\x1A\x45\xDF\xA3"):
content_type = "video/webm"
@@ -371,6 +386,10 @@ def ffprobe_f(filename):
return json.loads(out)
+def call_wrap(args):
+ subprocess.check_output(args, stderr=subprocess.STDOUT)
+
+
def getThumbDimensions(width, height, maxsize):
"""
Calculate dimensions to use for a thumbnail with maximum width/height of
diff --git a/cgi/manage.py b/cgi/manage.py
index f21e7e8..5def5c5 100644
--- a/cgi/manage.py
+++ b/cgi/manage.py
@@ -3,9 +3,8 @@ import _mysql
import os
import cgi
import shutil
-import imaplib
-import poplib
import datetime
+import logging
from database import *
from settings import Settings
@@ -40,8 +39,8 @@ def manage(self, path_split):
str(timestamp() - 604800)) # one week
else:
page += _('Incorrect username/password.')
- logAction('', 'Failed log-in. U:'+_mysql.escape_string(
- self.formdata['username'])+' IP:'+self.environ["REMOTE_ADDR"])
+ logAction('', 'Failed log-in. U:'+_mysql.escape_string(self.formdata['username'])+' IP logged.')
+ logging.warn("Failed log-in. U:{} IP:{}".format(self.formdata['username'], self.environ["REMOTE_ADDR"]))
else:
# Validate existing session
manage_cookie = getCookie(self, 'weabot_manage')
@@ -1117,7 +1116,7 @@ def manage(self, path_split):
# Table
if 'board' in self.formdata.keys() and self.formdata['board'] != 'all':
cboard = self.formdata['board']
- posts = FetchAll("SELECT posts.id, posts.timestamp, timestamp_formatted, IS_DELETED, INET6_NTOA(posts.ip) as ip, posts.message, dir, boardid FROM `posts` INNER JOIN `boards` ON boardid = boards.id WHERE `dir` = '%s' AND IS_DELETED %s ORDER BY `timestamp` DESC LIMIT %d, %d" % (
+ posts = FetchAll("SELECT posts.id, posts.timestamp, timestamp_formatted, IS_DELETED, INET6_NTOA(posts.ip) AS ip, posts.message, dir, boardid FROM `posts` INNER JOIN `boards` ON boardid = boards.id WHERE `dir` = '%s' AND IS_DELETED %s ORDER BY `timestamp` DESC LIMIT %d, %d" % (
_mysql.escape_string(self.formdata['board']), _mysql.escape_string(type_condition), currentpage*pagesize, pagesize))
try:
totals = FetchOne("SELECT COUNT(id) FROM `posts` WHERE IS_DELETED %s AND `boardid` = %s" % (
@@ -1126,7 +1125,7 @@ def manage(self, path_split):
skip = True
else:
cboard = 'all'
- posts = FetchAll("SELECT posts.id, posts.timestamp, timestamp_formatted, IS_DELETED, posts.ip, posts.message, dir FROM `posts` INNER JOIN `boards` ON boardid = boards.id WHERE IS_DELETED %s ORDER BY `timestamp` DESC LIMIT %d, %d" % (
+ posts = FetchAll("SELECT posts.id, posts.timestamp, timestamp_formatted, IS_DELETED, INET6_NTOA(posts.ip) AS ip, posts.message, dir FROM `posts` INNER JOIN `boards` ON boardid = boards.id WHERE IS_DELETED %s ORDER BY `timestamp` DESC LIMIT %d, %d" % (
_mysql.escape_string(type_condition), currentpage*pagesize, pagesize))
totals = FetchOne("SELECT COUNT(id) FROM `posts` WHERE IS_DELETED %s" %
_mysql.escape_string(type_condition), 0)
diff --git a/cgi/post.py b/cgi/post.py
index da2ad47..e27b971 100644
--- a/cgi/post.py
+++ b/cgi/post.py
@@ -7,6 +7,7 @@ import threading
import Queue
import _mysql
import formatting
+import logging
from database import *
from template import *
@@ -49,7 +50,7 @@ class Post(object):
return self.post
def insert(self):
- logTime("Insertando Post")
+ logging.info("Insertando Post")
post_values = []
for key, value in self.post.iteritems():
@@ -715,7 +716,7 @@ def deletePost(postid, password, deltype='0', imageonly=False, quick=False):
if int(post["parentid"]) == 0:
deleteReplies(post)
- logTime("Deleting post " + str(postid))
+ logging.info("Deleting post " + str(postid))
if deltype != '0' and post["parentid"] != '0':
# Soft delete (recycle bin)
UpdateDb("UPDATE `posts` SET `IS_DELETED` = %s WHERE `boardid` = %s AND `id` = %s LIMIT 1" % (deltype, board["id"], post["id"]))
@@ -788,7 +789,7 @@ def trimThreads():
"""
Delete any threads which have passed the MAX_THREADS setting
"""
- logTime("Trimming threads")
+ logging.debug("Trimming threads")
board = Settings._.BOARD
archived = False
@@ -1026,7 +1027,7 @@ def regenerateHome():
"""
Update index.html in the boards directory with useful data for users
"""
- logTime("Updating home")
+ logging.debug("Updating home")
t = datetime.datetime.now()
limit = Settings.HOME_LASTPOSTS
diff --git a/cgi/weabot.py b/cgi/weabot.py
index 3c5d0f5..f92e777 100755
--- a/cgi/weabot.py
+++ b/cgi/weabot.py
@@ -8,6 +8,7 @@ import time
import datetime
import random
import cgi
+import logging
import _mysql
from Cookie import SimpleCookie
@@ -33,6 +34,9 @@ _LOG = False
class weabot(object):
def __init__(self, environ, start_response):
global _DEBUG
+
+ logging.basicConfig(filename='weabot.log', format='%(asctime)s %(levelname)s %(message)s', level=logging.INFO)
+
self.environ = environ
if self.environ["PATH_INFO"].startswith("/weabot.py/"):
self.environ["PATH_INFO"] = self.environ["PATH_INFO"][11:]
@@ -48,7 +52,6 @@ class weabot(object):
'weabot', './locale', languages=[Settings.LANG])
lang.install()
- logTime("**Start**")
if _DEBUG:
import cProfile
@@ -61,6 +64,8 @@ class weabot(object):
except UserError, message:
self.error(message)
except Exception, inst:
+ logging.exception(inst)
+
import sys
import traceback
exc_type, exc_value, exc_traceback = sys.exc_info()
@@ -70,12 +75,6 @@ class weabot(object):
# close database and finish
CloseDb()
- logTime("**End**")
-
- if _LOG:
- logfile = open(Settings.ROOT_DIR + "weabot.txt", "w")
- logfile.write(logTimes())
- logfile.close()
def __iter__(self):
self.handleResponse()
@@ -732,8 +731,6 @@ class weabot(object):
if board["slip"] == '2':
if hide_end:
host = '★'
- elif addressIsTor(ip):
- host = 'onion'
else:
host = getHost(ip)
if host:
@@ -767,7 +764,7 @@ class weabot(object):
if hide_end or addressIsTor(ip):
country = '??'
else:
- country = getCountry(ip)
+ country = getCountry(ip) or '??'
post["name"] += " <em>[%s]</em>" % country
# set expiration date if necessary
@@ -812,7 +809,7 @@ class weabot(object):
post["message"]), _mysql.escape_string(board["id"]), _mysql.escape_string(str(postid))))
# do operations if replying to a thread (bump, autoclose, update cache)
- logTime("Updating thread")
+ logging.debug("Updating thread")
thread_length = None
if post["parentid"]:
# get length of the thread
@@ -1086,7 +1083,7 @@ if __name__ == "__main__":
# Psyco is not required, however it will be used if available
try:
import psyco
- logTime("Psyco se ha instalado")
+ logging.debug("Psyco se ha instalado")
psyco.bind(tenjin.helpers.to_str)
psyco.bind(weabot.run, 2)
psyco.bind(getFormData)