Merge pull request #108 from themotte/fix_profile_image_performance
Fix profile image performance
This commit is contained in:
commit
19cc4d3d6e
6 changed files with 111 additions and 33 deletions
|
@ -29,6 +29,30 @@ marked.use({
|
||||||
const content = this.parser.parseInline(token.tokens);
|
const content = this.parser.parseInline(token.tokens);
|
||||||
return `<span class="spoiler">${content}</span>`;
|
return `<span class="spoiler">${content}</span>`;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'mention',
|
||||||
|
level: 'inline',
|
||||||
|
start: function(src){
|
||||||
|
const match = src.match(/@[a-zA-Z0-9_\-]+/);
|
||||||
|
return match != null ? match.index : -1;
|
||||||
|
},
|
||||||
|
tokenizer: function(src) {
|
||||||
|
const rule = /^@[a-zA-Z0-9_\-]+/;
|
||||||
|
const match = rule.exec(src);
|
||||||
|
if(match){
|
||||||
|
return {
|
||||||
|
type: 'mention',
|
||||||
|
raw: match[0],
|
||||||
|
text: match[0].trim().slice(1),
|
||||||
|
tokens: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
renderer(token) {
|
||||||
|
const u = token.raw;
|
||||||
|
return `<a href="/${u}"><img src="/${u}/pic" class="pp20"> ${u}</a>`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
|
@ -63,6 +63,29 @@ def get_user(username, v=None, graceful=False):
|
||||||
|
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
def get_users(usernames, v=None, graceful=False):
|
||||||
|
if not usernames:
|
||||||
|
if not graceful: abort(404)
|
||||||
|
else: return []
|
||||||
|
|
||||||
|
def clean(n):
|
||||||
|
return n.replace('\\', '').replace('_', '\_').replace('%', '').strip()
|
||||||
|
|
||||||
|
usernames = [ clean(n) for n in usernames ]
|
||||||
|
|
||||||
|
users = g.db.query(User).filter(
|
||||||
|
or_(
|
||||||
|
User.username == any_(usernames),
|
||||||
|
User.original_username == any_(usernames)
|
||||||
|
)
|
||||||
|
).all()
|
||||||
|
|
||||||
|
if not users:
|
||||||
|
if not graceful: abort(404)
|
||||||
|
else: return []
|
||||||
|
|
||||||
|
return users
|
||||||
|
|
||||||
def get_account(id, v=None):
|
def get_account(id, v=None):
|
||||||
|
|
||||||
try: id = int(id)
|
try: id = int(id)
|
||||||
|
|
|
@ -144,29 +144,30 @@ def sanitize(sanitized, alert=False, comment=False, edit=False):
|
||||||
sanitized = sanitized.replace('','').replace('','').replace("\ufeff", "").replace("𒐪","")
|
sanitized = sanitized.replace('','').replace('','').replace("\ufeff", "").replace("𒐪","")
|
||||||
|
|
||||||
if alert:
|
if alert:
|
||||||
captured = []
|
matches = { g.group(1):g for g in mention_regex2.finditer(sanitized) if g }
|
||||||
for i in mention_regex2.finditer(sanitized):
|
users = get_users(matches.keys(),graceful=True)
|
||||||
if i.group(0) in captured: continue
|
|
||||||
captured.append(i.group(0))
|
|
||||||
|
|
||||||
u = get_user(i.group(1), graceful=True)
|
captured = []
|
||||||
|
for u in users:
|
||||||
if u:
|
if u:
|
||||||
sanitized = sanitized.replace(i.group(0), f'''<p><a href="/id/{u.id}"><img loading="lazy" src="/pp/{u.id}">@{u.username}</a>''')
|
i = matches.get(u.username) or matches.get(u.original_username)
|
||||||
|
if i.group(0) not in captured:
|
||||||
|
captured.append(i.group(0))
|
||||||
|
sanitized = sanitized.replace(i.group(0), f'''<p><a href="/id/{u.id}"><img loading="lazy" src="/pp/{u.id}">@{u.username}</a>''')
|
||||||
else:
|
else:
|
||||||
sanitized = reddit_regex.sub(r'\1<a href="https://old.reddit.com/\2" rel="nofollow noopener noreferrer">/\2</a>', sanitized)
|
sanitized = reddit_regex.sub(r'\1<a href="https://old.reddit.com/\2" rel="nofollow noopener noreferrer">/\2</a>', sanitized)
|
||||||
|
|
||||||
sanitized = sub_regex.sub(r'\1<a href="/\2">/\2</a>', sanitized)
|
sanitized = sub_regex.sub(r'\1<a href="/\2">/\2</a>', sanitized)
|
||||||
|
|
||||||
captured = []
|
matches = [ m for m in mention_regex.finditer(sanitized) if m ]
|
||||||
for i in mention_regex.finditer(sanitized):
|
names = set( m.group(2) for m in matches )
|
||||||
if i.group(0) in captured: continue
|
users = get_users(names,graceful=True)
|
||||||
captured.append(i.group(0))
|
|
||||||
|
|
||||||
u = get_user(i.group(2), graceful=True)
|
|
||||||
|
|
||||||
if u and (not (g.v and g.v.any_block_exists(u)) or g.v.admin_level > 1):
|
|
||||||
sanitized = sanitized.replace(i.group(0), f'''{i.group(1)}<a href="/id/{u.id}"><img loading="lazy" src="/pp/{u.id}">@{u.username}</a>''')
|
|
||||||
|
|
||||||
|
for u in users:
|
||||||
|
if not u: continue
|
||||||
|
m = [ m for m in matches if u.username == m.group(2) or u.original_username == m.group(2) ]
|
||||||
|
for i in m:
|
||||||
|
if not (g.v and g.v.any_block_exists(u)) or g.v.admin_level > 1:
|
||||||
|
sanitized = sanitized.replace(i.group(0), f'''{i.group(1)}<a href="/id/{u.id}"><img loading="lazy" src="/pp/{u.id}">@{u.username}</a>''', 1)
|
||||||
|
|
||||||
sanitized = imgur_regex.sub(r'\1_d.webp?maxwidth=9999&fidelity=high', sanitized)
|
sanitized = imgur_regex.sub(r'\1_d.webp?maxwidth=9999&fidelity=high', sanitized)
|
||||||
|
|
||||||
|
|
|
@ -1079,6 +1079,9 @@ def remove_follow(username, v):
|
||||||
|
|
||||||
return {"message": "Follower removed!"}
|
return {"message": "Follower removed!"}
|
||||||
|
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
import re
|
||||||
|
|
||||||
@app.get("/pp/<id>")
|
@app.get("/pp/<id>")
|
||||||
@app.get("/uid/<id>/pic")
|
@app.get("/uid/<id>/pic")
|
||||||
@app.get("/uid/<id>/pic/profile")
|
@app.get("/uid/<id>/pic/profile")
|
||||||
|
@ -1090,15 +1093,54 @@ def user_profile_uid(v, id):
|
||||||
try: id = int(id, 36)
|
try: id = int(id, 36)
|
||||||
except: abort(404)
|
except: abort(404)
|
||||||
|
|
||||||
x=get_account(id)
|
name = f"/pp/{id}"
|
||||||
return redirect(x.profile_url)
|
path = cache.get(name)
|
||||||
|
tout = 5 * 60 # 5 min
|
||||||
|
|
||||||
|
# if the path isn't cached then make it
|
||||||
|
if not path:
|
||||||
|
user = get_account(id)
|
||||||
|
path = urlparse(user.profile_url).path
|
||||||
|
cache.set(name,path,timeout=tout)
|
||||||
|
|
||||||
|
# if not found, search relative to the root
|
||||||
|
if not os.path.exists(path):
|
||||||
|
path = os.path.join(app.root_path,path.lstrip('/'))
|
||||||
|
cache.set(name,path,timeout=tout)
|
||||||
|
|
||||||
|
# if not found, fail
|
||||||
|
if not os.path.exists(path):
|
||||||
|
cache.set(name,None)
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
return send_file(path)
|
||||||
|
|
||||||
@app.get("/@<username>/pic")
|
@app.get("/@<username>/pic")
|
||||||
@limiter.exempt
|
@limiter.exempt
|
||||||
@auth_required
|
@auth_required
|
||||||
def user_profile_name(v, username):
|
def user_profile_name(v, username):
|
||||||
x = get_user(username)
|
|
||||||
return redirect(x.profile_url)
|
name = f"/@{username}/pic"
|
||||||
|
path = cache.get(name)
|
||||||
|
tout = 5 * 60 # 5 min
|
||||||
|
|
||||||
|
# if the path isn't cached then make it
|
||||||
|
if not path:
|
||||||
|
user = get_user(username)
|
||||||
|
path = urlparse(user.profile_url).path
|
||||||
|
cache.set(name,path,timeout=tout)
|
||||||
|
|
||||||
|
# if not found, search relative to the root
|
||||||
|
if not os.path.exists(path):
|
||||||
|
path = os.path.join(app.root_path,path.lstrip('/'))
|
||||||
|
cache.set(name,path,timeout=tout)
|
||||||
|
|
||||||
|
# if not found, fail
|
||||||
|
if not os.path.exists(path):
|
||||||
|
cache.set(name,None)
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
return send_file(path)
|
||||||
|
|
||||||
@app.get("/@<username>/saved/posts")
|
@app.get("/@<username>/saved/posts")
|
||||||
@auth_required
|
@auth_required
|
||||||
|
|
|
@ -111,7 +111,7 @@
|
||||||
required tabindex="5">
|
required tabindex="5">
|
||||||
<div class="custom-control custom-checkbox mt-4">
|
<div class="custom-control custom-checkbox mt-4">
|
||||||
<input autocomplete="off" type="checkbox" class="custom-control-input" id="termsCheck" required tabindex="6">
|
<input autocomplete="off" type="checkbox" class="custom-control-input" id="termsCheck" required tabindex="6">
|
||||||
<label class="custom-control-label terms" for="termsCheck">I accept the <a href="/sidebar" tabindex="8">rules</a></label>
|
<label class="custom-control-label terms" for="termsCheck">I accept the <a href="/rules" tabindex="8">rules</a></label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if hcaptcha %}
|
{% if hcaptcha %}
|
||||||
|
|
|
@ -12,8 +12,6 @@
|
||||||
<meta name="author" content="">
|
<meta name="author" content="">
|
||||||
<link rel="icon" type="image/png" href="/assets/images/{{SITE_NAME}}/icon.webp?v=1015">
|
<link rel="icon" type="image/png" href="/assets/images/{{SITE_NAME}}/icon.webp?v=1015">
|
||||||
|
|
||||||
{% set cc='Country Club' %}
|
|
||||||
|
|
||||||
{% block title %}
|
{% block title %}
|
||||||
<title>Create a post - {{SITE_NAME}}</title>
|
<title>Create a post - {{SITE_NAME}}</title>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -133,16 +131,6 @@
|
||||||
<label class="custom-control-label" for="private">Draft</label>
|
<label class="custom-control-label" for="private">Draft</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="custom-control custom-checkbox">
|
|
||||||
<input autocomplete="off" type="checkbox" class="custom-control-input" id="club" name="club">
|
|
||||||
<label class="custom-control-label" for="club">{{CC_TITLE}} thread</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="custom-control custom-checkbox">
|
|
||||||
<input onchange='draft(this);' autocomplete="off" type="checkbox" class="custom-control-input" id="ghost" name="ghost" {% if v.coins < 100 %}disabled{% endif %}>
|
|
||||||
<label class="custom-control-label" for="ghost">Ghost Thread (cost: 100 coins)</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue