mirror of https://gitlab.com/bashrc2/epicyon
Merge branch 'main' of gitlab.com:bashrc2/epicyon
commit
6638e7a6a4
|
@ -1085,6 +1085,7 @@ def save_media_in_form_post(media_bytes, debug: bool,
|
||||||
extension_list = {
|
extension_list = {
|
||||||
'png': 'image/png',
|
'png': 'image/png',
|
||||||
'jpeg': 'image/jpeg',
|
'jpeg': 'image/jpeg',
|
||||||
|
'jxl': 'image/jxl',
|
||||||
'gif': 'image/gif',
|
'gif': 'image/gif',
|
||||||
'svg': 'image/svg+xml',
|
'svg': 'image/svg+xml',
|
||||||
'webp': 'image/webp',
|
'webp': 'image/webp',
|
||||||
|
|
88
daemon.py
88
daemon.py
|
@ -366,7 +366,7 @@ from fitnessFunctions import fitness_performance
|
||||||
from fitnessFunctions import fitness_thread
|
from fitnessFunctions import fitness_thread
|
||||||
from fitnessFunctions import sorted_watch_points
|
from fitnessFunctions import sorted_watch_points
|
||||||
from fitnessFunctions import html_watch_points_graph
|
from fitnessFunctions import html_watch_points_graph
|
||||||
from siteactive import site_is_active
|
from siteactive import referer_is_active
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
@ -1060,16 +1060,65 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
system_language: str,
|
system_language: str,
|
||||||
project_version: str,
|
project_version: str,
|
||||||
custom_emoji: [],
|
custom_emoji: [],
|
||||||
show_node_info_accounts: bool) -> bool:
|
show_node_info_accounts: bool,
|
||||||
|
referer_domain: str,
|
||||||
|
debug: bool,
|
||||||
|
calling_site_timeout: int) -> bool:
|
||||||
"""This is a vestigil mastodon API for the purpose
|
"""This is a vestigil mastodon API for the purpose
|
||||||
of returning an empty result to sites like
|
of returning an empty result to sites like
|
||||||
https://mastopeek.app-dist.eu
|
https://mastopeek.app-dist.eu
|
||||||
"""
|
"""
|
||||||
if not path.startswith('/api/v1/'):
|
if not path.startswith('/api/v1/'):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if not referer_domain:
|
||||||
|
if not debug and not self.server.unit_test:
|
||||||
|
print('mastodon api request has no referer domain ' +
|
||||||
|
str(ua_str))
|
||||||
|
self._400()
|
||||||
|
return True
|
||||||
|
if referer_domain == self.server.domain_full:
|
||||||
|
print('mastodon api request from self')
|
||||||
|
self._400()
|
||||||
|
return True
|
||||||
|
if self.server.masto_api_is_active:
|
||||||
|
print('mastodon api is busy during request from ' +
|
||||||
|
referer_domain)
|
||||||
|
self._503()
|
||||||
|
return True
|
||||||
|
self.server.masto_api_is_active = True
|
||||||
|
# is this a real website making the call ?
|
||||||
|
if not debug and not self.server.unit_test and referer_domain:
|
||||||
|
# Does calling_domain look like a domain?
|
||||||
|
if ' ' in referer_domain or \
|
||||||
|
';' in referer_domain or \
|
||||||
|
'.' not in referer_domain:
|
||||||
|
print('mastodon api ' +
|
||||||
|
'referer does not look like a domain ' +
|
||||||
|
referer_domain)
|
||||||
|
self._400()
|
||||||
|
self.server.masto_api_is_active = False
|
||||||
|
return True
|
||||||
|
if not self.server.allow_local_network_access:
|
||||||
|
if local_network_host(referer_domain):
|
||||||
|
print('mastodon api referer domain is from the ' +
|
||||||
|
'local network ' + referer_domain)
|
||||||
|
self._400()
|
||||||
|
self.server.masto_api_is_active = False
|
||||||
|
return True
|
||||||
|
if not referer_is_active(http_prefix,
|
||||||
|
referer_domain, ua_str,
|
||||||
|
calling_site_timeout):
|
||||||
|
print('mastodon api referer url is not active ' +
|
||||||
|
referer_domain)
|
||||||
|
self._400()
|
||||||
|
self.server.masto_api_is_active = False
|
||||||
|
return True
|
||||||
|
|
||||||
print('mastodon api v1: ' + path)
|
print('mastodon api v1: ' + path)
|
||||||
print('mastodon api v1: authorized ' + str(authorized))
|
print('mastodon api v1: authorized ' + str(authorized))
|
||||||
print('mastodon api v1: nickname ' + str(nickname))
|
print('mastodon api v1: nickname ' + str(nickname))
|
||||||
|
print('mastodon api v1: referer ' + referer_domain)
|
||||||
self._update_known_crawlers(ua_str)
|
self._update_known_crawlers(ua_str)
|
||||||
|
|
||||||
broch_mode = broch_mode_is_active(base_dir)
|
broch_mode = broch_mode_is_active(base_dir)
|
||||||
|
@ -1108,10 +1157,12 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self._write(msg)
|
self._write(msg)
|
||||||
if send_json_str:
|
if send_json_str:
|
||||||
print(send_json_str)
|
print(send_json_str)
|
||||||
|
self.server.masto_api_is_active = False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# no api endpoints were matched
|
# no api endpoints were matched
|
||||||
self._404()
|
self._404()
|
||||||
|
self.server.masto_api_is_active = False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _masto_api(self, path: str, calling_domain: str,
|
def _masto_api(self, path: str, calling_domain: str,
|
||||||
|
@ -1125,17 +1176,19 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
system_language: str,
|
system_language: str,
|
||||||
project_version: str,
|
project_version: str,
|
||||||
custom_emoji: [],
|
custom_emoji: [],
|
||||||
show_node_info_accounts: bool) -> bool:
|
show_node_info_accounts: bool,
|
||||||
|
referer_domain: str, debug: bool) -> bool:
|
||||||
return self._masto_api_v1(path, calling_domain, ua_str, authorized,
|
return self._masto_api_v1(path, calling_domain, ua_str, authorized,
|
||||||
http_prefix, base_dir, nickname, domain,
|
http_prefix, base_dir, nickname, domain,
|
||||||
domain_full, onion_domain, i2p_domain,
|
domain_full, onion_domain, i2p_domain,
|
||||||
translate, registration, system_language,
|
translate, registration, system_language,
|
||||||
project_version, custom_emoji,
|
project_version, custom_emoji,
|
||||||
show_node_info_accounts)
|
show_node_info_accounts,
|
||||||
|
referer_domain, debug, 5)
|
||||||
|
|
||||||
def _nodeinfo(self, ua_str: str, calling_domain: str,
|
def _nodeinfo(self, ua_str: str, calling_domain: str,
|
||||||
referer_domain: str,
|
referer_domain: str,
|
||||||
httpPrefix: str, calling_site_timeout: int,
|
http_prefix: str, calling_site_timeout: int,
|
||||||
debug: bool) -> bool:
|
debug: bool) -> bool:
|
||||||
if self.path.startswith('/nodeinfo/1.0'):
|
if self.path.startswith('/nodeinfo/1.0'):
|
||||||
self._400()
|
self._400()
|
||||||
|
@ -1175,18 +1228,11 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.nodeinfo_is_active = False
|
self.server.nodeinfo_is_active = False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
referer_url = httpPrefix + '://' + referer_domain
|
if not referer_is_active(http_prefix,
|
||||||
if referer_domain + '/' in ua_str:
|
referer_domain, ua_str,
|
||||||
referer_url = referer_url + ua_str.split(referer_domain)[1]
|
calling_site_timeout):
|
||||||
if ' ' in referer_url:
|
|
||||||
referer_url = referer_url.split(' ')[0]
|
|
||||||
if ';' in referer_url:
|
|
||||||
referer_url = referer_url.split(';')[0]
|
|
||||||
if ')' in referer_url:
|
|
||||||
referer_url = referer_url.split(')')[0]
|
|
||||||
if not site_is_active(referer_url, calling_site_timeout):
|
|
||||||
print('nodeinfo referer url is not active ' +
|
print('nodeinfo referer url is not active ' +
|
||||||
referer_url)
|
referer_domain)
|
||||||
self._400()
|
self._400()
|
||||||
self.server.nodeinfo_is_active = False
|
self.server.nodeinfo_is_active = False
|
||||||
return True
|
return True
|
||||||
|
@ -6815,6 +6861,9 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
if 'image/avif' in self.headers['Accept']:
|
if 'image/avif' in self.headers['Accept']:
|
||||||
fav_type = 'image/avif'
|
fav_type = 'image/avif'
|
||||||
fav_filename = fav_filename.split('.')[0] + '.avif'
|
fav_filename = fav_filename.split('.')[0] + '.avif'
|
||||||
|
if 'image/jxl' in self.headers['Accept']:
|
||||||
|
fav_type = 'image/jxl'
|
||||||
|
fav_filename = fav_filename.split('.')[0] + '.jxl'
|
||||||
if not self.server.theme_name:
|
if not self.server.theme_name:
|
||||||
self.theme_name = get_config_param(base_dir, 'theme')
|
self.theme_name = get_config_param(base_dir, 'theme')
|
||||||
if not self.server.theme_name:
|
if not self.server.theme_name:
|
||||||
|
@ -6829,6 +6878,8 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
fav_filename = fav_filename.replace('.webp', '.ico')
|
fav_filename = fav_filename.replace('.webp', '.ico')
|
||||||
elif fav_filename.endswith('.avif'):
|
elif fav_filename.endswith('.avif'):
|
||||||
fav_filename = fav_filename.replace('.avif', '.ico')
|
fav_filename = fav_filename.replace('.avif', '.ico')
|
||||||
|
elif fav_filename.endswith('.jxl'):
|
||||||
|
fav_filename = fav_filename.replace('.jxl', '.ico')
|
||||||
if not os.path.isfile(favicon_filename):
|
if not os.path.isfile(favicon_filename):
|
||||||
# default favicon
|
# default favicon
|
||||||
favicon_filename = \
|
favicon_filename = \
|
||||||
|
@ -13910,7 +13961,9 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.system_language,
|
self.server.system_language,
|
||||||
self.server.project_version,
|
self.server.project_version,
|
||||||
self.server.custom_emoji,
|
self.server.custom_emoji,
|
||||||
self.server.show_node_info_accounts):
|
self.server.show_node_info_accounts,
|
||||||
|
referer_domain,
|
||||||
|
self.server.debug):
|
||||||
return
|
return
|
||||||
|
|
||||||
fitness_performance(getreq_start_time, self.server.fitness,
|
fitness_performance(getreq_start_time, self.server.fitness,
|
||||||
|
@ -18857,6 +18910,7 @@ def run_daemon(dyslexic_font: bool,
|
||||||
httpd.post_to_nickname = None
|
httpd.post_to_nickname = None
|
||||||
|
|
||||||
httpd.nodeinfo_is_active = False
|
httpd.nodeinfo_is_active = False
|
||||||
|
httpd.masto_api_is_active = False
|
||||||
|
|
||||||
httpd.dyslexic_font = dyslexic_font
|
httpd.dyslexic_font = dyslexic_font
|
||||||
|
|
||||||
|
|
|
@ -183,6 +183,7 @@ def meta_data_instance(showAccounts: bool,
|
||||||
'media_attachments': {
|
'media_attachments': {
|
||||||
'supported_mime_types': [
|
'supported_mime_types': [
|
||||||
'image/jpeg',
|
'image/jpeg',
|
||||||
|
'image/jxl',
|
||||||
'image/png',
|
'image/png',
|
||||||
'image/gif',
|
'image/gif',
|
||||||
'image/webp',
|
'image/webp',
|
||||||
|
|
|
@ -164,6 +164,7 @@ def _download_newswire_feed_favicon(session, base_dir: str,
|
||||||
'ico': 'x-icon',
|
'ico': 'x-icon',
|
||||||
'png': 'png',
|
'png': 'png',
|
||||||
'jpg': 'jpeg',
|
'jpg': 'jpeg',
|
||||||
|
'jxl': 'jxl',
|
||||||
'gif': 'gif',
|
'gif': 'gif',
|
||||||
'avif': 'avif',
|
'avif': 'avif',
|
||||||
'svg': 'svg+xml',
|
'svg': 'svg+xml',
|
||||||
|
|
|
@ -302,6 +302,7 @@ def post_message_to_outbox(session, translate: {},
|
||||||
|
|
||||||
extensions = {
|
extensions = {
|
||||||
"jpeg": "jpg",
|
"jpeg": "jpg",
|
||||||
|
"jxl": "jxl",
|
||||||
"gif": "gif",
|
"gif": "gif",
|
||||||
"svg": "svg",
|
"svg": "svg",
|
||||||
"webp": "webp",
|
"webp": "webp",
|
||||||
|
|
|
@ -140,6 +140,9 @@ def set_profile_image(base_dir: str, http_prefix: str,
|
||||||
elif image_filename.endswith('.avif'):
|
elif image_filename.endswith('.avif'):
|
||||||
media_type = 'image/avif'
|
media_type = 'image/avif'
|
||||||
icon_filename = icon_filename_base + '.avif'
|
icon_filename = icon_filename_base + '.avif'
|
||||||
|
elif image_filename.endswith('.jxl'):
|
||||||
|
media_type = 'image/jxl'
|
||||||
|
icon_filename = icon_filename_base + '.jxl'
|
||||||
elif image_filename.endswith('.svg'):
|
elif image_filename.endswith('.svg'):
|
||||||
media_type = 'image/svg+xml'
|
media_type = 'image/svg+xml'
|
||||||
icon_filename = icon_filename_base + '.svg'
|
icon_filename = icon_filename_base + '.svg'
|
||||||
|
|
|
@ -401,7 +401,7 @@ def post_image(session, attach_image_filename: str, federation_list: [],
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if not is_image_file(attach_image_filename):
|
if not is_image_file(attach_image_filename):
|
||||||
print('Image must be png, jpg, webp, avif, gif or svg')
|
print('Image must be png, jpg, jxl, webp, avif, gif or svg')
|
||||||
return None
|
return None
|
||||||
if not os.path.isfile(attach_image_filename):
|
if not os.path.isfile(attach_image_filename):
|
||||||
print('Image not found: ' + attach_image_filename)
|
print('Image not found: ' + attach_image_filename)
|
||||||
|
@ -415,6 +415,8 @@ def post_image(session, attach_image_filename: str, federation_list: [],
|
||||||
content_type = 'image/webp'
|
content_type = 'image/webp'
|
||||||
elif attach_image_filename.endswith('.avif'):
|
elif attach_image_filename.endswith('.avif'):
|
||||||
content_type = 'image/avif'
|
content_type = 'image/avif'
|
||||||
|
elif attach_image_filename.endswith('.jxl'):
|
||||||
|
content_type = 'image/jxl'
|
||||||
elif attach_image_filename.endswith('.svg'):
|
elif attach_image_filename.endswith('.svg'):
|
||||||
content_type = 'image/svg+xml'
|
content_type = 'image/svg+xml'
|
||||||
headers['Content-type'] = content_type
|
headers['Content-type'] = content_type
|
||||||
|
@ -469,6 +471,7 @@ def download_image(session, base_dir: str, url: str,
|
||||||
'png': 'png',
|
'png': 'png',
|
||||||
'jpg': 'jpeg',
|
'jpg': 'jpeg',
|
||||||
'jpeg': 'jpeg',
|
'jpeg': 'jpeg',
|
||||||
|
'jxl': 'jxl',
|
||||||
'gif': 'gif',
|
'gif': 'gif',
|
||||||
'svg': 'svg+xml',
|
'svg': 'svg+xml',
|
||||||
'webp': 'webp',
|
'webp': 'webp',
|
||||||
|
@ -575,6 +578,7 @@ def download_image_any_mime_type(session, url: str,
|
||||||
'ico': 'x-icon',
|
'ico': 'x-icon',
|
||||||
'png': 'png',
|
'png': 'png',
|
||||||
'jpg': 'jpeg',
|
'jpg': 'jpeg',
|
||||||
|
'jxl': 'jxl',
|
||||||
'jpeg': 'jpeg',
|
'jpeg': 'jpeg',
|
||||||
'gif': 'gif',
|
'gif': 'gif',
|
||||||
'svg': 'svg+xml',
|
'svg': 'svg+xml',
|
||||||
|
|
|
@ -120,3 +120,18 @@ def site_is_active(url: str, timeout: int) -> bool:
|
||||||
except BaseException:
|
except BaseException:
|
||||||
print('EX: site_is_active ' + str(loc))
|
print('EX: site_is_active ' + str(loc))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def referer_is_active(http_prefix: str,
|
||||||
|
referer_domain: str, ua_str: str,
|
||||||
|
calling_site_timeout: int) -> bool:
|
||||||
|
"""Returns true if the given referer is an active website
|
||||||
|
"""
|
||||||
|
referer_url = http_prefix + '://' + referer_domain
|
||||||
|
if referer_domain + '/' in ua_str:
|
||||||
|
referer_url = referer_url + ua_str.split(referer_domain)[1]
|
||||||
|
ending_chars = (' ', ';', ')')
|
||||||
|
for end_ch in ending_chars:
|
||||||
|
if end_ch in referer_url:
|
||||||
|
referer_url = referer_url.split(end_ch)[0]
|
||||||
|
return site_is_active(referer_url, calling_site_timeout)
|
||||||
|
|
9
utils.py
9
utils.py
|
@ -375,7 +375,7 @@ def get_audio_extensions() -> []:
|
||||||
def get_image_extensions() -> []:
|
def get_image_extensions() -> []:
|
||||||
"""Returns a list of the possible image file extensions
|
"""Returns a list of the possible image file extensions
|
||||||
"""
|
"""
|
||||||
return ('png', 'jpg', 'jpeg', 'gif', 'webp', 'avif', 'svg', 'ico')
|
return ('png', 'jpg', 'jpeg', 'gif', 'webp', 'avif', 'svg', 'ico', 'jxl')
|
||||||
|
|
||||||
|
|
||||||
def get_image_mime_type(image_filename: str) -> str:
|
def get_image_mime_type(image_filename: str) -> str:
|
||||||
|
@ -384,6 +384,7 @@ def get_image_mime_type(image_filename: str) -> str:
|
||||||
extensions_to_mime = {
|
extensions_to_mime = {
|
||||||
'png': 'png',
|
'png': 'png',
|
||||||
'jpg': 'jpeg',
|
'jpg': 'jpeg',
|
||||||
|
'jxl': 'jxl',
|
||||||
'gif': 'gif',
|
'gif': 'gif',
|
||||||
'avif': 'avif',
|
'avif': 'avif',
|
||||||
'svg': 'svg+xml',
|
'svg': 'svg+xml',
|
||||||
|
@ -402,6 +403,7 @@ def get_image_extension_from_mime_type(content_type: str) -> str:
|
||||||
image_media = {
|
image_media = {
|
||||||
'png': 'png',
|
'png': 'png',
|
||||||
'jpeg': 'jpg',
|
'jpeg': 'jpg',
|
||||||
|
'jxl': 'jxl',
|
||||||
'gif': 'gif',
|
'gif': 'gif',
|
||||||
'svg+xml': 'svg',
|
'svg+xml': 'svg',
|
||||||
'webp': 'webp',
|
'webp': 'webp',
|
||||||
|
@ -695,6 +697,8 @@ def save_json(json_object: {}, filename: str) -> bool:
|
||||||
def load_json(filename: str, delay_sec: int = 2, max_tries: int = 5) -> {}:
|
def load_json(filename: str, delay_sec: int = 2, max_tries: int = 5) -> {}:
|
||||||
"""Makes a few attempts to load a json formatted file
|
"""Makes a few attempts to load a json formatted file
|
||||||
"""
|
"""
|
||||||
|
if '/Actor@' in filename:
|
||||||
|
filename = filename.replace('/Actor@', '/inbox@')
|
||||||
json_object = None
|
json_object = None
|
||||||
tries = 0
|
tries = 0
|
||||||
while tries < max_tries:
|
while tries < max_tries:
|
||||||
|
@ -716,6 +720,8 @@ def load_json_onionify(filename: str, domain: str, onion_domain: str,
|
||||||
"""Makes a few attempts to load a json formatted file
|
"""Makes a few attempts to load a json formatted file
|
||||||
This also converts the domain name to the onion domain
|
This also converts the domain name to the onion domain
|
||||||
"""
|
"""
|
||||||
|
if '/Actor@' in filename:
|
||||||
|
filename = filename.replace('/Actor@', '/inbox@')
|
||||||
json_object = None
|
json_object = None
|
||||||
tries = 0
|
tries = 0
|
||||||
while tries < 5:
|
while tries < 5:
|
||||||
|
@ -2531,6 +2537,7 @@ def media_file_mime_type(filename: str) -> str:
|
||||||
'json': 'application/json',
|
'json': 'application/json',
|
||||||
'png': 'image/png',
|
'png': 'image/png',
|
||||||
'jpg': 'image/jpeg',
|
'jpg': 'image/jpeg',
|
||||||
|
'jxl': 'image/jxl',
|
||||||
'jpeg': 'image/jpeg',
|
'jpeg': 'image/jpeg',
|
||||||
'gif': 'image/gif',
|
'gif': 'image/gif',
|
||||||
'svg': 'image/svg+xml',
|
'svg': 'image/svg+xml',
|
||||||
|
|
|
@ -249,7 +249,8 @@ def _html_newswire(base_dir: str, newswire: {}, nickname: str, moderator: bool,
|
||||||
favicon_url = \
|
favicon_url = \
|
||||||
cached_favicon_filename.replace(base_dir, '')
|
cached_favicon_filename.replace(base_dir, '')
|
||||||
else:
|
else:
|
||||||
extensions = ('png', 'jpg', 'gif', 'avif', 'svg', 'webp')
|
extensions = \
|
||||||
|
('png', 'jpg', 'gif', 'avif', 'svg', 'webp', 'jxl')
|
||||||
for ext in extensions:
|
for ext in extensions:
|
||||||
cached_favicon_filename = \
|
cached_favicon_filename = \
|
||||||
get_fav_filename_from_url(base_dir, favicon_url)
|
get_fav_filename_from_url(base_dir, favicon_url)
|
||||||
|
|
|
@ -85,6 +85,9 @@ def html_login(css_cache: {}, translate: {},
|
||||||
elif os.path.isfile(base_dir + '/accounts/login.avif'):
|
elif os.path.isfile(base_dir + '/accounts/login.avif'):
|
||||||
login_image = 'login.avif'
|
login_image = 'login.avif'
|
||||||
login_image_filename = base_dir + '/accounts/' + login_image
|
login_image_filename = base_dir + '/accounts/' + login_image
|
||||||
|
elif os.path.isfile(base_dir + '/accounts/login.jxl'):
|
||||||
|
login_image = 'login.jxl'
|
||||||
|
login_image_filename = base_dir + '/accounts/' + login_image
|
||||||
|
|
||||||
if not login_image_filename:
|
if not login_image_filename:
|
||||||
login_image_filename = base_dir + '/accounts/' + login_image
|
login_image_filename = base_dir + '/accounts/' + login_image
|
||||||
|
|
|
@ -251,6 +251,7 @@ def update_avatar_image_cache(signing_priv_key_pem: str,
|
||||||
image_formats = {
|
image_formats = {
|
||||||
'png': 'png',
|
'png': 'png',
|
||||||
'jpg': 'jpeg',
|
'jpg': 'jpeg',
|
||||||
|
'jxl': 'jxl',
|
||||||
'jpeg': 'jpeg',
|
'jpeg': 'jpeg',
|
||||||
'gif': 'gif',
|
'gif': 'gif',
|
||||||
'svg': 'svg+xml',
|
'svg': 'svg+xml',
|
||||||
|
@ -998,7 +999,7 @@ def _is_attached_image(attachment_filename: str) -> bool:
|
||||||
if '.' not in attachment_filename:
|
if '.' not in attachment_filename:
|
||||||
return False
|
return False
|
||||||
image_ext = (
|
image_ext = (
|
||||||
'png', 'jpg', 'jpeg', 'webp', 'avif', 'svg', 'gif'
|
'png', 'jpg', 'jpeg', 'webp', 'avif', 'svg', 'gif', 'jxl'
|
||||||
)
|
)
|
||||||
ext = attachment_filename.split('.')[-1]
|
ext = attachment_filename.split('.')[-1]
|
||||||
if ext in image_ext:
|
if ext in image_ext:
|
||||||
|
|
Loading…
Reference in New Issue