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