|
''' |
|
|
|
This is a library for formatting GPT-4chan and chat outputs as nice HTML. |
|
|
|
''' |
|
|
|
import os |
|
import re |
|
from pathlib import Path |
|
|
|
from PIL import Image |
|
|
|
|
|
image_cache = {} |
|
|
|
def generate_basic_html(s): |
|
css = """ |
|
.container { |
|
max-width: 600px; |
|
margin-left: auto; |
|
margin-right: auto; |
|
background-color: rgb(31, 41, 55); |
|
padding:3em; |
|
} |
|
.container p { |
|
font-size: 16px !important; |
|
color: white !important; |
|
margin-bottom: 22px; |
|
line-height: 1.4 !important; |
|
} |
|
""" |
|
s = '\n'.join([f'<p>{line}</p>' for line in s.split('\n')]) |
|
s = f'<style>{css}</style><div class="container">{s}</div>' |
|
return s |
|
|
|
def process_post(post, c): |
|
t = post.split('\n') |
|
number = t[0].split(' ')[1] |
|
if len(t) > 1: |
|
src = '\n'.join(t[1:]) |
|
else: |
|
src = '' |
|
src = re.sub('>', '>', src) |
|
src = re.sub('(>>[0-9]*)', '<span class="quote">\\1</span>', src) |
|
src = re.sub('\n', '<br>\n', src) |
|
src = f'<blockquote class="message">{src}\n' |
|
src = f'<span class="name">Anonymous </span> <span class="number">No.{number}</span>\n{src}' |
|
return src |
|
|
|
def generate_4chan_html(f): |
|
css = """ |
|
|
|
#parent #container { |
|
background-color: #eef2ff; |
|
padding: 17px; |
|
} |
|
#parent #container .reply { |
|
background-color: rgb(214, 218, 240); |
|
border-bottom-color: rgb(183, 197, 217); |
|
border-bottom-style: solid; |
|
border-bottom-width: 1px; |
|
border-image-outset: 0; |
|
border-image-repeat: stretch; |
|
border-image-slice: 100%; |
|
border-image-source: none; |
|
border-image-width: 1; |
|
border-left-color: rgb(0, 0, 0); |
|
border-left-style: none; |
|
border-left-width: 0px; |
|
border-right-color: rgb(183, 197, 217); |
|
border-right-style: solid; |
|
border-right-width: 1px; |
|
border-top-color: rgb(0, 0, 0); |
|
border-top-style: none; |
|
border-top-width: 0px; |
|
color: rgb(0, 0, 0); |
|
display: table; |
|
font-family: arial, helvetica, sans-serif; |
|
font-size: 13.3333px; |
|
margin-bottom: 4px; |
|
margin-left: 0px; |
|
margin-right: 0px; |
|
margin-top: 4px; |
|
overflow-x: hidden; |
|
overflow-y: hidden; |
|
padding-bottom: 4px; |
|
padding-left: 2px; |
|
padding-right: 2px; |
|
padding-top: 4px; |
|
} |
|
|
|
#parent #container .number { |
|
color: rgb(0, 0, 0); |
|
font-family: arial, helvetica, sans-serif; |
|
font-size: 13.3333px; |
|
width: 342.65px; |
|
margin-right: 7px; |
|
} |
|
|
|
#parent #container .op { |
|
color: rgb(0, 0, 0); |
|
font-family: arial, helvetica, sans-serif; |
|
font-size: 13.3333px; |
|
margin-bottom: 8px; |
|
margin-left: 0px; |
|
margin-right: 0px; |
|
margin-top: 4px; |
|
overflow-x: hidden; |
|
overflow-y: hidden; |
|
} |
|
|
|
#parent #container .op blockquote { |
|
margin-left: 0px !important; |
|
} |
|
|
|
#parent #container .name { |
|
color: rgb(17, 119, 67); |
|
font-family: arial, helvetica, sans-serif; |
|
font-size: 13.3333px; |
|
font-weight: 700; |
|
margin-left: 7px; |
|
} |
|
|
|
#parent #container .quote { |
|
color: rgb(221, 0, 0); |
|
font-family: arial, helvetica, sans-serif; |
|
font-size: 13.3333px; |
|
text-decoration-color: rgb(221, 0, 0); |
|
text-decoration-line: underline; |
|
text-decoration-style: solid; |
|
text-decoration-thickness: auto; |
|
} |
|
|
|
#parent #container .greentext { |
|
color: rgb(120, 153, 34); |
|
font-family: arial, helvetica, sans-serif; |
|
font-size: 13.3333px; |
|
} |
|
|
|
#parent #container blockquote { |
|
margin: 0px !important; |
|
margin-block-start: 1em; |
|
margin-block-end: 1em; |
|
margin-inline-start: 40px; |
|
margin-inline-end: 40px; |
|
margin-top: 13.33px !important; |
|
margin-bottom: 13.33px !important; |
|
margin-left: 40px !important; |
|
margin-right: 40px !important; |
|
} |
|
|
|
#parent #container .message { |
|
color: black; |
|
border: none; |
|
} |
|
""" |
|
|
|
posts = [] |
|
post = '' |
|
c = -2 |
|
for line in f.splitlines(): |
|
line += "\n" |
|
if line == '-----\n': |
|
continue |
|
elif line.startswith('--- '): |
|
c += 1 |
|
if post != '': |
|
src = process_post(post, c) |
|
posts.append(src) |
|
post = line |
|
else: |
|
post += line |
|
if post != '': |
|
src = process_post(post, c) |
|
posts.append(src) |
|
|
|
for i in range(len(posts)): |
|
if i == 0: |
|
posts[i] = f'<div class="op">{posts[i]}</div>\n' |
|
else: |
|
posts[i] = f'<div class="reply">{posts[i]}</div>\n' |
|
|
|
output = '' |
|
output += f'<style>{css}</style><div id="parent"><div id="container">' |
|
for post in posts: |
|
output += post |
|
output += '</div></div>' |
|
output = output.split('\n') |
|
for i in range(len(output)): |
|
output[i] = re.sub(r'^(>(.*?)(<br>|</div>))', r'<span class="greentext">\1</span>', output[i]) |
|
output[i] = re.sub(r'^<blockquote class="message">(>(.*?)(<br>|</div>))', r'<blockquote class="message"><span class="greentext">\1</span>', output[i]) |
|
output = '\n'.join(output) |
|
|
|
return output |
|
|
|
def get_image_cache(path): |
|
cache_folder = Path("cache") |
|
if not cache_folder.exists(): |
|
cache_folder.mkdir() |
|
|
|
mtime = os.stat(path).st_mtime |
|
if (path in image_cache and mtime != image_cache[path][0]) or (path not in image_cache): |
|
img = Image.open(path) |
|
img.thumbnail((200, 200)) |
|
output_file = Path(f'cache/{path.name}_cache.png') |
|
img.convert('RGB').save(output_file, format='PNG') |
|
image_cache[path] = [mtime, output_file.as_posix()] |
|
|
|
return image_cache[path][1] |
|
|
|
def generate_chat_html(history, name1, name2, character): |
|
css = """ |
|
.chat { |
|
margin-left: auto; |
|
margin-right: auto; |
|
max-width: 800px; |
|
height: 66.67vh; |
|
overflow-y: auto; |
|
padding-right: 20px; |
|
display: flex; |
|
flex-direction: column-reverse; |
|
} |
|
|
|
.message { |
|
display: grid; |
|
grid-template-columns: 60px 1fr; |
|
padding-bottom: 25px; |
|
font-size: 15px; |
|
font-family: Helvetica, Arial, sans-serif; |
|
line-height: 1.428571429; |
|
} |
|
|
|
.circle-you { |
|
width: 50px; |
|
height: 50px; |
|
background-color: rgb(238, 78, 59); |
|
border-radius: 50%; |
|
} |
|
|
|
.circle-bot { |
|
width: 50px; |
|
height: 50px; |
|
background-color: rgb(59, 78, 244); |
|
border-radius: 50%; |
|
} |
|
|
|
.circle-bot img, .circle-you img { |
|
border-radius: 50%; |
|
width: 100%; |
|
height: 100%; |
|
object-fit: cover; |
|
} |
|
|
|
.text { |
|
} |
|
|
|
.text p { |
|
margin-top: 5px; |
|
} |
|
|
|
.username { |
|
font-weight: bold; |
|
} |
|
|
|
.message-body { |
|
} |
|
|
|
.message-body img { |
|
max-width: 300px; |
|
max-height: 300px; |
|
border-radius: 20px; |
|
} |
|
|
|
.message-body p { |
|
margin-bottom: 0 !important; |
|
font-size: 15px !important; |
|
line-height: 1.428571429 !important; |
|
} |
|
|
|
.dark .message-body p em { |
|
color: rgb(138, 138, 138) !important; |
|
} |
|
|
|
.message-body p em { |
|
color: rgb(110, 110, 110) !important; |
|
} |
|
|
|
""" |
|
|
|
output = '' |
|
output += f'<style>{css}</style><div class="chat" id="chat">' |
|
img = '' |
|
|
|
for i in [ |
|
f"characters/{character}.png", |
|
f"characters/{character}.jpg", |
|
f"characters/{character}.jpeg", |
|
"img_bot.png", |
|
"img_bot.jpg", |
|
"img_bot.jpeg" |
|
]: |
|
|
|
path = Path(i) |
|
if path.exists(): |
|
img = f'<img src="file/{get_image_cache(path)}">' |
|
break |
|
|
|
img_me = '' |
|
for i in ["img_me.png", "img_me.jpg", "img_me.jpeg"]: |
|
path = Path(i) |
|
if path.exists(): |
|
img_me = f'<img src="file/{get_image_cache(path)}">' |
|
break |
|
|
|
for i,_row in enumerate(history[::-1]): |
|
row = _row.copy() |
|
row[0] = re.sub(r"(\*\*)([^\*\n]*)(\*\*)", r"<b>\2</b>", row[0]) |
|
row[1] = re.sub(r"(\*\*)([^\*\n]*)(\*\*)", r"<b>\2</b>", row[1]) |
|
row[0] = re.sub(r"(\*)([^\*\n]*)(\*)", r"<em>\2</em>", row[0]) |
|
row[1] = re.sub(r"(\*)([^\*\n]*)(\*)", r"<em>\2</em>", row[1]) |
|
p = '\n'.join([f"<p>{x}</p>" for x in row[1].split('\n')]) |
|
output += f""" |
|
<div class="message"> |
|
<div class="circle-bot"> |
|
{img} |
|
</div> |
|
<div class="text"> |
|
<div class="username"> |
|
{name2} |
|
</div> |
|
<div class="message-body"> |
|
{p} |
|
</div> |
|
</div> |
|
</div> |
|
""" |
|
|
|
if not (i == len(history)-1 and len(row[0]) == 0): |
|
p = '\n'.join([f"<p>{x}</p>" for x in row[0].split('\n')]) |
|
output += f""" |
|
<div class="message"> |
|
<div class="circle-you"> |
|
{img_me} |
|
</div> |
|
<div class="text"> |
|
<div class="username"> |
|
{name1} |
|
</div> |
|
<div class="message-body"> |
|
{p} |
|
</div> |
|
</div> |
|
</div> |
|
""" |
|
|
|
output += "</div>" |
|
return output |
|
|