litechan/tsuchinoko.py

1086 lines
44 KiB
Python

from flask import Flask, redirect, url_for, render_template, render_template_string, request, send_file, send_from_directory, session
import yaml, cv2
from PIL import Image
from datetime import date, datetime
import time
from pprint import pprint
from random import choice
from os.path import exists
from os import remove
import re
import marklite
from hashlib import sha256
import hwip
from io import BytesIO
from base64 import b64encode
from nceicon import open_cei
app = Flask(__name__)
app.config['MAX_CONTENT_LENGTH'] = 10 * 1024 * 1024
app.secret_key = 'Glyphoglossus Molossus'
app.config['SESSION_TYPE'] = 'filesystem'
### funs to encode cei files for pyodide
def pad(to_pad, n):
while len(to_pad)<n:
to_pad = "0"+to_pad
return to_pad
def bytes_to_string(bobj):
string = ""
for byte in bobj:
string+=pad(hex(byte).split("x",1)[1],2)
return string
def string_to_bytes(sobj):
bobj = bytes([])
for n in range(0,len(sobj),2):
bobj+=bytes([int(sobj[n:n+2],16)])
return bobj
### end of funs
@app.before_request
def make_session_permanent():
session.permanent = True
@app.route("/noko/<text>")
def serve_img_noko(text):
img = hwip.sign(text)
img_io = BytesIO()
img.save(img_io, "PNG")
img_io.seek(0)
dataurl = '<img src="'+'data:image/png;base64,' + b64encode(img_io.getvalue()).decode('ascii')+'">'
return dataurl
@app.route("/ip/")
def get_ip():
return process_ip(request)
def process_ip(request):
if request.environ.get('HTTP_X_FORWARDED_FOR') is None:
addr = request.environ['REMOTE_ADDR']
else:
addr = request.environ['HTTP_X_FORWARDED_FOR']
aaddr = addr.split(",")[0]
try:
obj = bytes([int(i) for i in aaddr.split(".")])
sha = sha256(obj).hexdigest()
except Exception:
obj = bytes(aaddr, "utf-8")
sha = sha256(obj).hexdigest()
return sha
@app.route("/favicon.ico/")
def favicon():
return render_template_string("""<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">""")
@app.route("/")
def io_manager_index():
data = load("templates/index.html")
prev = ""
post_list = []
if exists("preview.yml"):
with open("preview.yml") as f:
post_list = yaml.safe_load(f.read())
for i in range(len(post_list)):
post_list[i]["text"] = post_list[i].get("text") if len(post_list[i].get("text"))<81 else post_list[i].get("text")[:80-3]+"..."
return render_template_string(data, preview=post_list)
@app.route("/getcei/<fname>")
def getcei(fname):
path = f"cei/{fname}"
if exists(path):
return send_file(path, as_attachment=True)
else:
return "<b>Error: Invalid Filename</b>"
@app.route("/liteshare/<fname>")
def liteshare(fname):
path = f"liteshare/{fname}"
if exists(path):
return send_file(path, as_attachment=True)
else:
return "<b>Error: Invalid Filename</b>"
@app.route("/admin/<input>")
def admin_actions(input):
series = input.split("-")
if series[0]=="setadmin":
password = series[1]
if password==load("admin.txt"):
with open("admin.yml", "w") as f:
f.write(yaml.dump({"admin": process_ip(request)}))
elif password==load("typh.txt"):
d = yaml.safe_load(load("admin.yml"))
d["typh"] = process_ip(request)
with open("admin.yml", "w") as f:
f.write(yaml.dump(d))
elif series[0]=="ban":
d = yaml.safe_load(load("admin.yml"))
hurts_when_ip = [d[key] for key in list(d.keys())]
if process_ip(request) in hurts_when_ip:
command, thread, post = series
thread, post = [int(i) for i in [thread, post]]
my_list = yaml.safe_load(load(f"{thread}.tp"))["posts"]+yaml.safe_load(load(f"{thread}.tp"))["pinned"]
post_data = [i for i in my_list if i["id"]==post][0]
expires = int(time.time()) + 60*60*24*3
if not exists("banned.yml"):
with open("banned.yml", "w") as f:
f.write(yaml.dump({"test": 0}))
blacklist = yaml.safe_load(load("banned.yml"))
blacklist[post_data["alias"]] = expires
with open("banned.yml", "w") as f:
f.write(yaml.dump(blacklist))
else:
return redirect("/post/ran-6-1")
elif series[0]=="del":
tpdict = yaml.safe_load(load(f"{series[1]}.tp"))
tplist = tpdict["posts"]
postdict = [p for p in tplist if p["id"]==int(series[2])][0]
d = yaml.safe_load(load("admin.yml"))
hurts_when_ip = [d[key] for key in list(d.keys())] + [postdict["alias"]]
#hurts_when_ip = [yaml.safe_load(load("admin.yml"))["admin"],postdict["alias"]]
if not process_ip(request) in hurts_when_ip:
return redirect("/post/ran-6-1")
else:
tpdict["posts"] = [p for p in tplist if p["id"]!=int(series[2])]
with open(f"{series[1]}.tp", "w") as f:
f.write(yaml.dump(tpdict))
if postdict["media"]:
for key in ["media","preview","thumbnail"]:
try:
remove(postdict[key])
except Exception:
pass
return redirect("/post/ran-5-1")
@app.route("/post/<info>")
def post_info(info):
board, action, link = info.split("-",2)
additional = "<p><img src='/static/banned.png' width=400></p>" if action=="4" else ""
actions = {"1": "Posted Successfully",
"2": "Incorrect Captcha",
"3": "Unsupported File Format",
"4": "You're banned",
"5": "Admin action issued",
"6": "Error Issuing the Admin Command"}
action = actions[action]
post = load("templates/post.html")
return render_template_string(post.replace("{{additional}}", additional), action=action, link="/"+board+"/"+link), {"Refresh": "3; url="+"/"+board+"/"+link}
@app.route("/cei/<file_path>")
def load_cei(file_path):
file_path = f"cei/{file_path}"
cei = bytes_to_string(open(file_path,"rb").read())#str(list(open(file_path,"rb").read()))
template = """<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/pyodide/v0.22.1/full/pyodide.js"></script>
</head>
<body>
<p id="title">Loading modules...</p>
<p><img id="cei-image"></p>
<a href='/ceiget/"""+file_path.rsplit("/",1)[-1]+"""' download='"""+file_path.rsplit("/",1)[-1]+"""'>Download</a>
<a href='/liteshare/ceicon.zip' download="ceicon.zip">Get the CEI Viewer</a>
<script>
var ceibytes = '"""+cei+"""';
async function main() {
let pyodide = await loadPyodide();
await pyodide.loadPackage(["pillow","numpy","opencv-python","pyyaml"]);
document.getElementById("title").innerHTML = "Decoding image...";
pyodide.runPython(`
from js import document, ceibytes
from PIL import Image
import numpy as np
import cv2
import zlib
import yaml
from io import BytesIO
from base64 import b64encode
def get_handle(text):
if not isinstance(text, str):
text = " ".join(text)
if text[0].lower()!=text[0].upper():
delimit = " "
else:
delimit = text[0]
text = text[1:]
d = {"tags": []}
for tag in text.split(delimit):
if ":" in tag:
key, value = tag.split(":",1)
d[key] = value
else:
d["tags"].append(tag)
d["tags"] = " ".join(d["tags"])
if "name" in list(d.keys()) and "author" in list(d.keys()):
base = f"{d['name']} by {d['author']}"
elif "name" in list(d.keys()):
base = f"{d['name']}"
else:
base = "CEI file"
additional = ", tags: "+d["tags"] if d["tags"] else ""
return base+additional
def string_to_bytes(sobj):
bobj = bytes([])
for n in range(0,len(sobj),2):
bobj+=bytes([int(sobj[n:n+2],16)])
return bobj
def np_LSS_procedural(LSS, obj = []):
L, S_1, S_2 = cv2.split(LSS.astype(np.float32))
vnp_lss = np.vectorize(np_lss)
parts = 16
obj[:] = [None for i in range(parts)]
length = LSS.shape[0]
L_list = [L[int(length*i/parts):int(length*(i+1)/parts),:] for i in range(parts)]
S_1_list = [S_1[int(length*i/parts):int(length*(i+1)/parts),:] for i in range(parts)]
S_2_list = [S_2[int(length*i/parts):int(length*(i+1)/parts),:] for i in range(parts)]
for n in range(parts):
l, s1, s2 = [i[n] for i in [L_list,S_1_list,S_2_list]]
merged = cv2.merge(vnp_lss(l,s1,s2))
obj[n] = Image.fromarray(merged.astype(np.uint8))
img_io = BytesIO()
obj[n].save(img_io, "PNG")
img_io.seek(0)
dataurl = 'data:image/png;base64,' + b64encode(img_io.getvalue()).decode('ascii')
document.getElementById(f"cei-image-{n}").src = dataurl
def from_ibyte(ibyte, size):
ib = bin(ibyte).split("b")[-1]
while len(ib)<8:
ib = "0"+ib
volume = (int(ib[:3], base = 2)+1)*(int(ib[-4:-1], base = 2)+1)
x = 16 if not int(ib[3]) else size[0]%16
y = 16 if not int(ib[-1]) else size[1]%16
return (x,y), ((int(ib[:3], base = 2)+1),(int(ib[-4:-1], base = 2)+1)), volume
def cei_dict(bd):
l = sepget(bd)
d = dict()
for n in range(0, len(l), 2):
match str(l[n], "utf-8"):
case "version":
value = int.from_bytes(l[n+1], "big")
case "size" | "csize":
value = tuple([int.from_bytes(i, "big") for i in sepget(l[n+1])])
case "luminocity" | "chromaticity" | "palette":
value = zlib.decompress(l[n+1])
case "tags":
value = str(l[n+1], "utf-8")#tuple([str(i, "utf-8") for i in sepget(l[n+1])])
d[str(l[n],"utf-8")] = value
return d
def sepget(sequence):
items = []
while len(sequence):
prelen = sequence[-1]
sequence = sequence[:-1]
length = int.from_bytes(sequence[-prelen:], "big")
sequence = sequence[:-len(sequence[-prelen:])]
items = [sequence[-length:]] + items
sequence = sequence[:-length]
return items
def open_cei(fread, preview = False):
d = cei_dict(fread)
luma = d["luminocity"]
focus = 0
sectors = []
luma_bg = np.ndarray((d["size"][1],d["size"][0]), dtype = np.uint8)
while focus<len(luma):
sec_size, red_size, volume = from_ibyte(luma[focus], d["size"])
bytes_ = luma[focus+1:focus+volume+1]
#print(red_size)
array = np.array([int(i) for i in bytes_], dtype = np.uint8).reshape((red_size[1], red_size[0]))
sectors.append(cv2.resize(array, sec_size, interpolation = cv2.INTER_LINEAR))
focus+=volume+1
for y in range(0, d["size"][1], 16):
for x in range(0, d["size"][0], 16):
sector = sectors.pop(0)
h, w = sector.shape
luma_bg[y:y+h,x:x+w] = sector
#cv2.imshow("img",luma_bg)
if preview:
return Image.fromarray(luma_bg), d["tags"]
pal_ind = 0
palette = dict()
for n in range(0, len(d["palette"]), 2):
palette[pal_ind] = tuple(d["palette"][n:n+2])
pal_ind+=1
colors = np.array([0,palette[0][0],palette[0][1]], dtype = np.uint8)#[]
csize = d["csize"]
for n, index in enumerate(d["chromaticity"]):
if n:
colors = np.append(colors, np.array([0,palette[index][0],palette[index][1]], dtype = np.uint8), axis = 0)
lss = colors.reshape(csize[1],csize[0],3)
lss = cv2.resize(lss, d["size"], interpolation = cv2.INTER_LINEAR)
lss[:,:,0] = luma_bg
return np_LSS(lss), d["tags"]
def np_lss(L,S_1,S_2):
try:
s1 = S_1/(255-S_1)
except ZeroDivisionError:
s1 = S_1
try:
s2 = (255-S_2)/S_2
except ZeroDivisionError:
s2 = 255
G = 3*L / ( s1 + s2 + 1 )#source[n*3]
try:
R = (S_1*G)/(255-S_1)
except ZeroDivisionError:
R = (S_1*G)/1
try:
B = G*(255-S_2)/S_2
except ZeroDivisionError:
B = G*(255-S_2)/1
maximal = max([R,G,B])
if maximal > 255:
multiplier = 255/maximal
R,G,B = R*multiplier,G*multiplier,B*multiplier
R,G,B = round(R),round(G),round(B)
return R,G,B
def np_LSS(LSS):#convert lss back to rgb
L, S_1, S_2 = cv2.split(LSS.astype(np.float32))
vnp_lss = np.vectorize(np_lss)
merged = cv2.merge(vnp_lss(L,S_1,S_2))
return Image.fromarray(merged.astype(np.uint8))
if __name__=="__main__":
pil_im, text = open_cei(string_to_bytes(ceibytes))
img_io = BytesIO()
pil_im.save(img_io, "PNG")
img_io.seek(0)
dataurl = 'data:image/png;base64,' + b64encode(img_io.getvalue()).decode('ascii')
document.getElementById("cei-image").src = dataurl
document.getElementById("title").innerHTML = get_handle(text);
`);
};
main();
</script>
</body>
</html>"""
return render_template_string(template)
@app.route("/<p1>/", methods=['post', 'get'])
def io_manager_board(p1):
params = p1+"/"
r = Request(token = process_ip(request),
captcha = request.form.get('security_id'),
content = request.form.get('content'),
file = request.files.get('my_file'),
type = request.method,
params = params)
return r.process()
@app.route("/<p1>/<p2>", methods=['post', 'get'])
def io_manager_thread(p1, p2):
params = p1+"/"+p2
r = Request(token = process_ip(request),
captcha = request.form.get('security_id'),
content = request.form.get('content'),
file = request.files.get('my_file'),
type = request.method,
params = params)
return r.process()
class Request:
def __init__(self, token, captcha, content, file, type, params):
self.token = token
self.captcha = captcha
self.content = content
self.file = file
self.type = type
self.params = params
pdict = parse_params(self.params)
self.pdict = pdict
if pdict["type"]=="frontpage":
self.page = ""
elif pdict["type"]=="board":
self.page = Thread(self)
elif pdict["type"]=="catalog":
self.page = Catalog(self)
elif pdict["type"]=="thread":
self.page = Thread(self)
elif pdict["type"]=="post":
self.page = Post(self)
def process(self):
if self.type=="POST":
return self.make_post_new()
return render_template_string(self.page.render())
def make_post(self):
if self.pdict["type"]=="board":
fname = f"{get_post_number()}.tp"
me, pr, th = process_media(self.file)
increase_post_number()
d = {"alias": self.token,
"id": get_post_number(),
"name": "Anonymous",
"thumbnail": th,
"preview": pr,
"media": me,
"text": self.content,
"date": str(date.today().strftime("%d/%m/%Y"))+" "+datetime.now().strftime("%H:%M:%S")}
bfile = self.pdict["board"]+".b"
with open(bfile, "r") as f:
thread_list = yaml.safe_load(f.read())
thread_list["posts"] = [fname]+thread_list["posts"]
if thread_list==None:
thread_list = []
with open(bfile, "w") as f:
f.write(yaml.dump(thread_list))
with open(fname, "w") as f:
f.write(yaml.dump([d]))
return_board = self.pdict["board"]
return_thread = ""
elif self.pdict["type"]=="thread":
fname = f"{self.pdict['index']}.tp"
me, pr, th = process_media(self.file)
increase_post_number()
d = {"alias": self.token,
"id": get_post_number(),
"name": "Anonymous",
"thumbnail": th,
"preview": pr,
"media": me,
"text": self.content,
"date": str(date.today().strftime("%d/%m/%Y"))+" "+datetime.now().strftime("%H:%M:%S")}
print("fname:",fname)
with open(fname, "r") as f:
tp = yaml.safe_load(f.read())
tp["posts"]+=[d]
with open(fname, "w") as f:
f.write(yaml.dump(tp))
return_board = self.pdict["board"]
return_thread = self.pdict["thread"]
return redirect("/post/"+f"{return_board}-1-{return_thread}")
def make_post_new(self):
if exists("banned.yml"):
banned = yaml.safe_load(load("banned.yml"))
if self.token in list(banned.keys()):
if time.time() < banned[self.token]:
return_board = self.pdict["board"]
return_thread = self.pdict["thread"]
return redirect("/post/"+f"{return_board}-4-{return_thread}")
else:
banned.pop(self.token)
with open("banned.yml", "w") as f:
f.write(yaml.dump(banned))
if self.pdict["type"] == "board":
if not "legacy":
fname = f"{get_post_number()}.tp"
me, pr, th = process_media(self.file)
increase_post_number()
d = {"alias": self.token,
"id": get_post_number(),
"name": "Anonymous",
"thumbnail": th,
"preview": pr,
"media": me,
"text": self.content,
"date": str(date.today().strftime("%d/%m/%Y"))+" "+datetime.now().strftime("%H:%M:%S"),
"board": self.pdict["board"]
}
bfile = self.pdict["board"]+".b"
with open(bfile, "r") as f:
thread_list = yaml.safe_load(f.read())
thread_list["posts"] = [fname]+thread_list["posts"]
if thread_list==None:
thread_list = []
with open(bfile, "w") as f:
f.write(yaml.dump(thread_list))
with open(fname, "w") as f:
f.write(yaml.dump([d]))
return_board = self.pdict["board"]
return_thread = ""
else:
fname = f"{get_post_number()}.tp"
me, pr, th = process_media(self.file)
increase_post_number()
d = {"alias": self.token,
"id": get_post_number(),
"name": "Anonymous",
"thumbnail": th,
"preview": pr,
"media": me,
"text": self.content,
"date": str(date.today().strftime("%d/%m/%Y"))+" "+datetime.now().strftime("%H:%M:%S"),
"board": self.pdict["board"]
}
bfile = self.pdict["board"]+".b"
with open(bfile, "r") as f:
thread_dict = yaml.safe_load(f.read())
if thread_dict==None:
thread_dict = {"pinned": [], "posts": []}
thread_dict["posts"] = [fname]+thread_dict["posts"]
with open(bfile, "w") as f:
f.write(yaml.dump(thread_dict))
with open(fname, "w") as f:
f.write(yaml.dump({"pinned": [d],"posts": []}))
return_board = self.pdict["board"]
return_thread = ""
elif self.pdict["type"]=="thread":
fname = f"{self.pdict['index']}.tp"
self.bump(f"{self.pdict['board']}.b",fname)
me, pr, th = process_media(self.file)
increase_post_number()
d = {"alias": self.token,
"id": get_post_number(),
"name": "Anonymous",
"thumbnail": th,
"preview": pr,
"media": me,
"text": self.content,
"date": str(date.today().strftime("%d/%m/%Y"))+" "+datetime.now().strftime("%H:%M:%S"),
"board": self.pdict["board"],
"thread": self.pdict["index"]
}
with open(fname, "r") as f:
tp = yaml.safe_load(f.read())
tp["posts"]+=[d]
with open(fname, "w") as f:
f.write(yaml.dump(tp))
return_board = self.pdict["board"]
return_thread = self.pdict["thread"]
if not "nsfw" in self.pdict["board"]:#preview in main page modification
if exists("preview.yml"):
base_list = yaml.safe_load(open("preview.yml").read())
else:
base_list = []
base_list.append(d)
if len(base_list)>3:
base_list.pop(0)
with open("preview.yml", "w") as f:
f.write(yaml.dump(base_list))
return redirect("/post/"+f"{return_board}-1-{return_thread}")
def bump(self, bname, tname):
board_dict = yaml.safe_load(load(bname))
if tname in board_dict["pinned"]:
board_dict["pinned"].pop(board_dict["pinned"].index(tname))
board_dict["pinned"] = [tname] + board_dict["pinned"]
elif tname in board_dict["posts"]:
board_dict["posts"].pop(board_dict["posts"].index(tname))
board_dict["posts"] = [tname] + board_dict["posts"]
with open(bname, "w") as f:
f.write(yaml.dump(board_dict))
class Catalog:
def __init__(self, master):
self.master = master
def render(self):
with open("templates/catalog.html") as f:
base = f.read()
row = "<tr> <td>{}</td> <td>{}</td> <td>{}</td> <td>{}</td> </tr>"
if self.master.pdict["type"]=="catalog":
post_info_names = get_posts_from_yaml(f"{self.master.pdict['board']}.b")
post_info = [merge(get_posts_from_yaml(post_info_name)[0] , {"thread": post_info_name.split(".")[0]}) for post_info_name in post_info_names]
rows = []
while post_info:
four = []
for _ in range(4):
if post_info:
four.append(Post(self, post_info.pop(0)).render_for_catalog())
else:
four.append("")
rows.append(row.format(*four))
return render_template_string( load("templates/anonymous6.html").replace("{{board}}",self.get_catalog()).replace("{{tables}}",render_template_string(base.replace("{{TABLE_ROWS}}","\n".join(rows)))),
dt = datetime.now().strftime('%d/%m/%Y %H:%M:%S')+random_arquee(), board_title = yaml.safe_load(load(f"{self.master.pdict['board']}.b")).get("title"))
def get_catalog(self):
html = f"""<font color="#47292b">
<font size="6" style="font-size: 28pt">
<span lang="en-US">
<a style="color:#954f72;" href="/{self.master.pdict["board"]}/">
<b>/{self.master.pdict["board"]}/</b></a>
</span>
</font>
</font>"""
return html
class Thread:
def __init__(self, master):
self.master = master
def render(self):
with open("templates/anonymous6.html") as f:
base = f.read().replace("{{board}}",self.get_catalog())
if self.master.pdict["type"]=="board":
post_info_names = get_posts_from_yaml(f"{self.master.pdict['board']}.b")#yaml.safe_load(load(f"{self.master.pdict['board']}.b"))
post_info = [merge(get_posts_from_yaml(post_info_name)[0] , {"thread": post_info_name.split(".")[0]}) for post_info_name in post_info_names]
elif self.master.pdict["type"]=="thread":
post_info = [merge(pi , {"thread": f"{self.master.pdict['thread']}"}) for pi in get_posts_from_yaml(f"{self.master.pdict['thread']}.tp")]
return render_template_string(base.replace("{{tables}}","\n".join([Post(self, pi).render() for pi in post_info]+self.get_login())),
dt = datetime.now().strftime('%d/%m/%Y %H:%M:%S')+random_arquee(), board_title = yaml.safe_load(load(f"{self.master.pdict['board']}.b")).get("title"))
def get_login(self):
login = """<p></p><p align='center'>{% block captcha %}{% if """+str(captcha_enabled())+""" %}<img width=400 height=90
src='"""+serve_captcha("static/"+self.master.token+".jpg")+"""' v:shapes="IMG">{% endif %}"""+"""{% endblock captcha %}<form align='center' action="" method="post" enctype = "multipart/form-data" style=style="text-size-adjust:none">
</p><div style= "margin-left:auto;margin-right:auto;align:center;width:100%;text-align:center">
<p>
<table>
<tr>
"""+"""{% block log %}{% if """+str(captcha_enabled())+""" %}<td style= "width:20%"><label>Captcha: </label></td>
<td style= "width:80%"><input style= "width:100%" type="password" name="security_id"></td>{% endif %}{% endblock log %}"""+"""
</tr>
</table>
</p>
<p align="center">
<textarea {ta} style= "width:100%" name="content" rows="10" cols="40" id="maintextarea"></textarea>
{div}
</p>
<p>
<input name="my_file" type="file">
</p>
<p>
<input type="submit">
</p></div>
</form>""".format(ta = """onKeyDown="textCounter(this,'progressbar1',2000)"
onKeyUp="textCounter(this,'progressbar1',2000)"
onFocus="textCounter(this,'progressbar1',2000)""", div = """<div style= "margin-left:auto;margin-right:auto;align:center;width:100%" id="progressbar1" class="progress"></div>
<script>textCounter(document.getElementById("maxcharfield"),"progressbar1",2000)</script>""")
return [login]
def get_catalog(self):
html = f"""<font color="#47292b">
<font size="6" style="font-size: 28pt">
<span lang="en-US">
<a style="color:#954f72;" href="/{self.master.pdict["board"]}/catalog">
<b>/{self.master.pdict["board"]}/</b></a>
</span>
</font>
</font>"""
return html
class Post:
def __init__(self, master, info):
#print("Info:", info["thread"])
self.info = info
self.master = master
def render(self):
text = self.info["text"]
text = emojis(text)
dots = '''<div class="dropdown">
<button class="dropbtn"><span lang="EN-US" style="mso-ansi-language:EN-US;padding:0cm 5.4pt 0cm 5.4pt">...<o:p></o:p></span></button>
<div class="dropdown-content">
<a href="/admin/del-{}-{}"><span lang="EN-US" style="mso-ansi-language:EN-US;padding:0cm 5.4pt 0cm 5.4pt">Delete post<o:p></o:p></span></a>
<a href="{}"><span lang="EN-US" style="mso-ansi-language:EN-US;padding:0cm 5.4pt 0cm 5.4pt">View original<o:p></o:p></span></a>
<a href="/admin/ban-{}-{}"><span lang="EN-US" style="mso-ansi-language:EN-US;padding:0cm 5.4pt 0cm 5.4pt">Ban poster<o:p></o:p></span></a>
<a onclick="javascript:var mta = document.getElementById('maintextarea');mta.value += '#{}\\n';textCounter(mta,'progressbar1',2000);"><span lang="EN-US" style="mso-ansi-language:EN-US;padding:0cm 5.4pt 0cm 5.4pt">Reply<o:p></o:p></span></a>
</div>
</div>'''.format(self.info["thread"],self.info["id"],"/"+self.info["media"],self.info["thread"],self.info["id"],self.info["id"])#board
if 0:
regular = """<p style="text-align:justify">{text}</p>"""#{thread_link} #7C7C7C
green = """<p style="color:#70AD47;text-align:justify">>{text}</p>"""
base_link = """<a href={thread_link} style:"color:#7c7c7c;background-color: transparent;text-decoration:none"><span style="text-align:justify">{text}</span></a>"""
title_form = """<b><p style="font-size:18.0pt;text-align:justify">{title}</p></b>"""
processed_text = ""
focus = 0
while focus<len(text):
if text[focus] in "#[>\n":
if text[focus]=="#":
start = focus+1
end = start
while end<len(text) and text[end] in "0123456789":
end+=1
link = f"/{self.master.master.pdict['board']}/{self.info['thread']}#{text[start:end]}"
high_text = "#"+text[start:end]
processed_text+=base_link.format(thread_link = link, text = high_text)
focus = end
elif text[focus]=="[":
end = text.find("]", focus)
processed_text+=title_form.format(title = text[focus+1:end])
focus = end+1
elif text[focus]=="\n":
processed_text+="<p></p>"
focus+=1
elif (focus==0 or text[focus-1]=="\n") and text[focus]==">":
end = focus+1
while end<len(text) and text[end]!="\n":
end+=1
processed_text+=green.format(text = text[focus+1:end])
focus = end+1
elif text[focus]==">":
processed_text+=text[focus]
focus+=1
else:
processed_text+=text[focus]
focus+=1
else:
processed_text = marklite.render(text, {"board":self.master.master.pdict['board'],"thread":self.info['thread']})
self.is_video = False##########
#processed_text = emojis(processed_text)
self.table_info = {"div_id": self.info["id"],
"x": 200,
"y": 200,
"image": self.get_popup(),
"headercolor": "#ecddbe",
"bgcolor": "#fff",
"dots": dots,
"name": self.info["name"],
"date": time_label(self.info["date"]),
"number": self.get_number(),
"content": processed_text}
table = self.get_table()
return table
def get_number(self):
inside = "#"+str(self.info["id"])
base = """<a href="{thread_link}" style:"color:#7c7c7c;background-color: transparent;text-decoration:none"><span style="text-align:justify">{text}</span></a>"""
return base.format(thread_link = f"/{self.master.master.pdict['board']}/{self.info['thread']}", text = inside)
def render_for_catalog(self):
regular = """<span style="text-align:justify">{text}</span>"""#{thread_link} #7C7C7C
green = """<p style="color:#70AD47;text-align:justify">>{text}</p>"""
base_link = """<a href={thread_link} style:"color:#7c7c7c;background-color: transparent;text-decoration:none"><span style="text-align:justify">{text}</span></a>"""
title_form = """<b><p style="font-size:18.0pt;text-align:justify">{title}</p></b>"""
base = load("templates/catalog_post.html")
text = self.info["text"]# if len(self.info["text"])<25 else self.info["text"][:25]
self.info["board"] = self.master.master.pdict["board"]
#text = emojis(text)
processed_text = ""
focus = 0
while focus<len(text) and focus<25:
#print("inside focus", focus, text[focus])
if text[focus] in "#[>\n":
if text[focus]=="#":
start = focus+1
end = start+1
while end<len(text) and text[end] in "0123456789":
end+=1
link = f"/{self.master.master.pdict['board']}/{self.info['thread']}-{text[start:end]}"
high_text = text[start:end]
processed_text+=base_link.format(thread_link = link, text = high_text)
focus = end+1
elif text[focus]=="[":
end = text.find("]", focus)
processed_text+=title_form.format(title = text[focus+1:end])
focus = end+1 if end!=-1 else focus+1
elif text[focus]=="\n":
processed_text+="<p></p>"
focus+=1
else:
end = focus+1
while end<len(text) and text[end]!="\n":
end+=1
processed_text+=green.format(text = text[focus+1:end])
focus = end+1
else:
processed_text+=text[focus]
focus+=1
#base = render_template_string(base, number = )
processed_text = emojis(processed_text)
return render_template_string(base.replace("{{number}}",self.get_number()).replace("{{title}}",processed_text), thread_link = f"/{self.info['board']}/"+str(self.info["thread"]), image = "/"+self.info["thumbnail"])
def get_table(self):
table = """<div id="{div_id}" class="post">
<table class = "posttable" bgcolor="{bgcolor}" align="center" border="0" cellspacing="0" cellpadding="0">
<tr style="background:#ecddbe;">
<td class="postname"><span style="padding-right:5px">{dots}</span><span>{name}</span></td><td style="width:60%"><table width="100%"><td>{date}</td><td align="right">{number}</td></table></td>
</tr>
<tr class="postbody">
<td width="100%" colspan="2"><div style="padding: 5px;"><span style="width:100%;">{image}</span><p style="width:100%;">{content}</p></div></td>
</tr>
</table>
</div>""".format(**self.table_info)
return table
def get_popup(self):
print(self.info["media"])
is_video = (self.info["media"].lower().endswith(".mp4") or self.info["media"].lower().endswith(".webm"))
if not is_video:
template = load("templates/popup_universal.html")
print("image")
else:
print("video")
template = load("templates/popup_universal.html")
if self.info["media"]:
if not is_video:
return render_template_string(template, hash_id = self.info["id"],
thumbnail = "/"+self.info["thumbnail"],
preview = "/"+self.info["preview"],
fname = self.info["media"],
original_link = "/"+self.info["media"])
else:
return render_template_string(template, hash_id = self.info["id"],
thumbnail = "/"+self.info["thumbnail"],
preview = "/"+self.info["media"],
fname = self.info["media"],
original_link = "/"+self.info["media"])
else:
return ""
def get_posts_from_yaml(fname):
try:
data = yaml.safe_load(load(fname))
if isinstance(data, dict):
if data["pinned"]:
return data["pinned"]+data["posts"]
else:
return data["posts"]
elif isinstance(data, list):
return data
else:
raise TypeError("")
except Exception as e:
return []
def random_arquee():
with open("marquee.data") as f:
data = yaml.safe_load(f.read())
return " «"+render_template_string(choice(data))+"»"
def time_label(data):
html = """<style>
/* Container for the text that expands on hover */
.expanded-text {
width: 100%;
display: inline-block;
}
/* Longer name hidden by default */
span.longer-name{
display:none;
}
/* On hover, hide the short name */
.expanded-text:hover span.short-name{
display:none;
}
/* On hover, display the longer name. */
.expanded-text:hover span.longer-name{
display:block;
}
</style><span align ="left" class="expanded-text">
<span class="short-name"><span>{short}</span></span>
<span class="longer-name"><span>{full_}</span></span></span>""".replace("{short}",data.split(" ")[0]).replace("{full_}", data)#.format(short=data.split(" ")[0], full=data)
return html
def serve_captcha(filename):
return ""
def captcha_enabled():
return False
def get_popup_legacy(pngs):
one, two, three = [load("templates/"+i) for i in ["pop1.html","pop4.html","pop3.html"]]
images = []
for png in pngs:
tag = "video" if ".mp4" in png else "img"
if ".png" in png:
images.append(render_template_string(two, item_hash = str(abs(hash(png))), file_jpg = png.replace(".png",".jpg"),
uri=serve_pil_image("static/"+png), png=png, full="/static/"+png.split(".")[0]+".png", tag=tag))
else:
images.append(render_template_string(two, item_hash = str(abs(hash(png))), file_jpg = png.replace(".png",".jpg"),
uri=serve_pil_image("static/"+png), png=png, full="/static/"+png.split(".")[0]+".png", tag=tag))
return one, images[0], three#"\n".join([one,*images,three])
def get_popup_video_legacy(pngs):
one, two, three = [load("templates/"+i) for i in ["pop1.html","pop5.html","pop3.html"]]
images = []
for png in pngs:
tag = "video" if ".mp4" in png else "img"
if ".mp4" in png or 1:
images.append(render_template_string(two, item_hash = str(abs(hash(png))), file_jpg = png.replace(".mp4",".jpg"),
uri=serve_pil_image("static/"+png), png=png, full="/static/"+png.split(".")[0]+".mp4", tag = tag))
else:
images.append(render_template_string(two, item_hash = str(abs(hash(png))), file_jpg = png.replace(".mp4",".jpg")))
return one, images[0], three#"\n".join([one,*images,three])
def load(filename):
with open(filename) as f:
output = f.read()
return output
def emojis(text):
sequence = text.split(":")
if len(sequence)<3:
return text
for n in range(1,len(sequence),2):
if set(sequence[n]).issubset(set("_qwertyuiopasdfghjklzxcvbnm0123456789")):
sequence[n] = '''<img class="emoji" height="36" '''+f'title=":{sequence[n]}:"'+'''src="{{url_for('static','''+f'filename="emoji.{sequence[n]}.png"'+''')}}">'''
else:
sequence[n] = ":"+sequence[n]+":"
return "".join(sequence)
def emojis2(text):
pattern_list = re.findall("(?<=:)[a-zA-Z]+(?=:)", text)
for pat in pattern_list:
print(pat)
if exists(f"static/emoji.{pat}.png"):
emoji = '''<img class="emoji" height="36" '''+f'title=":{pat}:"'+'''src="{{url_for('static','''+f'filename="emoji.{pat}.png"'+''')}}">'''
text = text.replace(f":{pat}:", emoji, 1)
else:
pass
return text
def parse_params(params):
plist = params.split("/")
d = dict()
if len(plist)==1 and not plist[0]:#frontpage
d["type"] = "frontpage"
elif len(plist)==1:#board
d["type"] = "board"
d["board"] = plist[0]
elif len(plist)==2:#thread or catalog
if plist[-1].isnumeric():
d["type"] = "thread"
d["thread"] = plist[1]
d["index"] = plist[-1]
elif plist[-1]=="catalog":
d["type"] = "catalog"
else:
d["type"] = "board"
d["board"] = plist[0]
else:#post
d["thread"] = plist[1]
d["post"] = plist[2]
d["type"] = "post"
print(d)
return d
def get_post_number():
with open("data/post_number.data") as f:
n = int(f.read())
return n
def increase_post_number():
n = get_post_number()+1
with open("data/post_number.data", "w") as f:
f.write(str(n))
def to_pil(imgOpenCV):
return Image.fromarray(cv2.cvtColor(imgOpenCV, cv2.COLOR_BGR2RGB))
def video_thumbnail(fname):
vidcap = cv2.VideoCapture(fname)
video_length = int(vidcap.get(cv2.CAP_PROP_FRAME_COUNT))
success,image = vidcap.read()
count = 0
while success and count<0.25*video_length:
success, image = vidcap.read()
count += 1
pimg = to_pil(image)
pimg.thumbnail((200,200))
return pimg
def process_media(file):
fname = "static/"+file.filename
ext = fname.split(".")[-1].lower()
supported_images = ["png",
"jpg",
"jpeg",
"jpp",
"gif",
"webp",
"apng",
"tiff"]
supported_videos = ["mp4",
"webm"]
supported_formats = supported_images + supported_videos + ["cei"]
if not ext in supported_formats:
return "", "", ""
else:
if ext in supported_images:
file.save(fname)
original = Image.open(fname)
preview = fname+".preview.jpg"
original.convert("RGB").save(preview, quality = 90, optimize = True, progressive = True)
thumbnail = fname+".thumbnail.jpg"
th = original.copy().convert("RGB")
th.thumbnail((200,200))
th.save(thumbnail, quality = 75, optimize = True, progressive = True)
elif ext=="cei":
fname = "cei/"+file.filename
file.save(fname)
original = open_cei(fname, True)[0]
preview = "static/"+file.filename+".preview.jpg"
original.convert("RGB").save(preview, quality = 90, optimize = True, progressive = True)
thumbnail = "static/"+file.filename+".thumbnail.jpg"
th = original.copy().convert("RGB")
th.thumbnail((200,200))
th.save(thumbnail, quality = 75, optimize = True, progressive = True)
elif ext in supported_videos:
file.save(fname)
preview = fname
th = video_thumbnail(fname)
thumbnail = fname+".thumbnail.jpg"
th.save(thumbnail, quality = 75, optimize = True, progressive = True)
return fname, preview, thumbnail
def ip_name(ip):
originals = list(".1234567890")
modified = list("qwertyuiopa")
name = ""
for character in ip:
name += modified[originals.index(character)]
return str(abs(hash(name)))
def merge(dict1, dict2):
dict3 = dict1.copy()
dict3.update(dict2)
return dict3
def tag_correction(html):
htmlist = html.split("<!-- mod -->")
for n in range(len(htmlist)):
element = htmlist[n]
important = element.split("<!-- endmod -->")[0]
if "<video" in important and not (".mp4" in important or ".webm" in important):
element = element.replace("<video","<img",1).replace("</video>","",1)
htmlist[n] = element
elif "<img" in important and (".mp4" in important or ".webm" in important):
element = element.replace("<img","<video",1)
htmlist[n] = element
print("corrected")
return "<!-- mod -->".join(htmlist)
"""
- alias: 8e3qe31u4up91451
date: 08/07/2022 10:44:44
id: '914'
image: 4730.png
name: Anonymous
text: <3
"""
if __name__ == "__main__":
app.run(threaded=True)#http://127.0.0.1:5000/ran/1