mirror of
https://github.com/YunoHost/apps.git
synced 2024-09-03 20:06:07 +02:00
Use pathlib, fix ruff issues
This commit is contained in:
parent
b44b882474
commit
9d9748a218
5 changed files with 110 additions and 107 deletions
|
@ -0,0 +1 @@
|
||||||
|
#!/usr/bin/env python3
|
104
store/app.py
104
store/app.py
|
@ -1,45 +1,39 @@
|
||||||
import time
|
#!/usr/bin/env python3
|
||||||
import re
|
|
||||||
import toml
|
|
||||||
import base64
|
import base64
|
||||||
import hashlib
|
import hashlib
|
||||||
import hmac
|
import hmac
|
||||||
import os
|
|
||||||
import string
|
|
||||||
import random
|
import random
|
||||||
import urllib
|
import re
|
||||||
import json
|
import string
|
||||||
import sys
|
import sys
|
||||||
from slugify import slugify
|
import time
|
||||||
from flask import (
|
import urllib
|
||||||
Flask,
|
from pathlib import Path
|
||||||
send_from_directory,
|
|
||||||
render_template,
|
import toml
|
||||||
session,
|
from flask import Flask, redirect, render_template, request, send_from_directory, session
|
||||||
redirect,
|
from flask.typing import ResponseReturnValue
|
||||||
request,
|
from flask_babel import Babel # type: ignore
|
||||||
)
|
|
||||||
from flask_babel import Babel
|
|
||||||
from flask_babel import gettext as _
|
from flask_babel import gettext as _
|
||||||
from github import Github, InputGitAuthor
|
from github import Github, InputGitAuthor
|
||||||
|
from slugify import slugify
|
||||||
|
|
||||||
sys.path = [os.path.dirname(__file__)] + sys.path
|
from .utils import (
|
||||||
|
|
||||||
from utils import (
|
|
||||||
get_locale,
|
|
||||||
get_catalog,
|
|
||||||
get_wishlist,
|
|
||||||
get_stars,
|
|
||||||
get_app_md_and_screenshots,
|
|
||||||
save_wishlist_submit_for_ratelimit,
|
|
||||||
check_wishlist_submit_ratelimit,
|
check_wishlist_submit_ratelimit,
|
||||||
|
get_app_md_and_screenshots,
|
||||||
|
get_catalog,
|
||||||
|
get_locale,
|
||||||
|
get_stars,
|
||||||
|
get_wishlist,
|
||||||
|
save_wishlist_submit_for_ratelimit,
|
||||||
)
|
)
|
||||||
|
|
||||||
app = Flask(__name__, static_url_path="/assets", static_folder="assets")
|
app = Flask(__name__, static_url_path="/assets", static_folder="assets")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
config = toml.loads(open("config.toml").read())
|
config = toml.loads(Path("config.toml").open().read())
|
||||||
except Exception as e:
|
except RuntimeError:
|
||||||
print(
|
print(
|
||||||
"You should create a config.toml with the appropriate key/values, cf config.toml.example"
|
"You should create a config.toml with the appropriate key/values, cf config.toml.example"
|
||||||
)
|
)
|
||||||
|
@ -126,8 +120,8 @@ def popularity_json():
|
||||||
@app.route("/app/<app_id>")
|
@app.route("/app/<app_id>")
|
||||||
def app_info(app_id):
|
def app_info(app_id):
|
||||||
infos = get_catalog()["apps"].get(app_id)
|
infos = get_catalog()["apps"].get(app_id)
|
||||||
app_folder = os.path.join(config["APPS_CACHE"], app_id)
|
app_folder = Path(config["APPS_CACHE"]) / app_id
|
||||||
if not infos or not os.path.exists(app_folder):
|
if not infos or not app_folder.exists():
|
||||||
return f"App {app_id} not found", 404
|
return f"App {app_id} not found", 404
|
||||||
|
|
||||||
get_app_md_and_screenshots(app_folder, infos)
|
get_app_md_and_screenshots(app_folder, infos)
|
||||||
|
@ -144,7 +138,7 @@ def app_info(app_id):
|
||||||
|
|
||||||
|
|
||||||
@app.route("/app/<app_id>/<action>")
|
@app.route("/app/<app_id>/<action>")
|
||||||
def star_app(app_id, action):
|
def star_app(app_id: str, action) -> ResponseReturnValue:
|
||||||
assert action in ["star", "unstar"]
|
assert action in ["star", "unstar"]
|
||||||
if app_id not in get_catalog()["apps"] and app_id not in get_wishlist():
|
if app_id not in get_catalog()["apps"] and app_id not in get_wishlist():
|
||||||
return _("App %(app_id) not found", app_id=app_id), 404
|
return _("App %(app_id) not found", app_id=app_id), 404
|
||||||
|
@ -153,26 +147,23 @@ def star_app(app_id, action):
|
||||||
_("You must be logged in to be able to star an app")
|
_("You must be logged in to be able to star an app")
|
||||||
+ "<br/><br/>"
|
+ "<br/><br/>"
|
||||||
+ _(
|
+ _(
|
||||||
"Note that, due to various abuses, we restricted login on the app store to 'trust level 1' users.<br/><br/>'Trust level 1' is obtained after interacting a minimum with the forum, and more specifically: entering at least 5 topics, reading at least 30 posts, and spending at least 10 minutes reading posts."
|
"Note that, due to various abuses, we restricted login on the app store to 'trust level 1' users."
|
||||||
|
"<br/><br/>"
|
||||||
|
"'Trust level 1' is obtained after interacting a minimum with the forum, and more specifically: "
|
||||||
|
"entering at least 5 topics, reading at least 30 posts, and spending at least 10 minutes reading posts."
|
||||||
),
|
),
|
||||||
401,
|
401,
|
||||||
)
|
)
|
||||||
|
|
||||||
app_star_folder = os.path.join(".stars", app_id)
|
app_star_folder = Path(".stars") / app_id
|
||||||
app_star_for_this_user = os.path.join(
|
app_star_for_this_user = app_star_folder / (session.get("user", {})["id"])
|
||||||
".stars", app_id, session.get("user", {})["id"]
|
|
||||||
)
|
|
||||||
|
|
||||||
if not os.path.exists(app_star_folder):
|
app_star_folder.mkdir(exist_ok=True)
|
||||||
os.mkdir(app_star_folder)
|
|
||||||
|
|
||||||
if action == "star":
|
if action == "star":
|
||||||
open(app_star_for_this_user, "w").write("")
|
app_star_for_this_user.open("w").write("")
|
||||||
elif action == "unstar":
|
elif action == "unstar":
|
||||||
try:
|
app_star_folder.unlink(missing_ok=True)
|
||||||
os.remove(app_star_for_this_user)
|
|
||||||
except FileNotFoundError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if app_id in get_catalog()["apps"]:
|
if app_id in get_catalog()["apps"]:
|
||||||
return redirect(f"/app/{app_id}")
|
return redirect(f"/app/{app_id}")
|
||||||
|
@ -203,7 +194,10 @@ def add_to_wishlist():
|
||||||
_("You must be logged in to submit an app to the wishlist")
|
_("You must be logged in to submit an app to the wishlist")
|
||||||
+ "<br/><br/>"
|
+ "<br/><br/>"
|
||||||
+ _(
|
+ _(
|
||||||
"Note that, due to various abuses, we restricted login on the app store to 'trust level 1' users.<br/><br/>'Trust level 1' is obtained after interacting a minimum with the forum, and more specifically: entering at least 5 topics, reading at least 30 posts, and spending at least 10 minutes reading posts."
|
"Note that, due to various abuses, we restricted login on the app store to 'trust level 1' users."
|
||||||
|
"<br/><br/>'Trust level 1' is obtained after interacting a minimum with the forum, and more "
|
||||||
|
"specifically: entering at least 5 topics, reading at least 30 posts, and spending at least "
|
||||||
|
"10 minutes reading posts."
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return render_template(
|
return render_template(
|
||||||
|
@ -258,7 +252,8 @@ def add_to_wishlist():
|
||||||
check_wishlist_submit_ratelimit(session["user"]["username"]) is True
|
check_wishlist_submit_ratelimit(session["user"]["username"]) is True
|
||||||
and session["user"]["bypass_ratelimit"] is False,
|
and session["user"]["bypass_ratelimit"] is False,
|
||||||
_(
|
_(
|
||||||
"Proposing wishlist additions is limited to once every 15 days per user. Please try again in a few days."
|
"Proposing wishlist additions is limited to once every 15 days per user. "
|
||||||
|
"Please try again in a few days."
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(len(name) >= 3, _("App name should be at least 3 characters")),
|
(len(name) >= 3, _("App name should be at least 3 characters")),
|
||||||
|
@ -298,7 +293,8 @@ def add_to_wishlist():
|
||||||
for keyword in boring_keywords_to_check_for_people_not_reading_the_instructions
|
for keyword in boring_keywords_to_check_for_people_not_reading_the_instructions
|
||||||
),
|
),
|
||||||
_(
|
_(
|
||||||
"Please focus on what the app does, without using marketing, fuzzy terms, or repeating that the app is 'free' and 'self-hostable'."
|
"Please focus on what the app does, without using marketing, fuzzy terms, "
|
||||||
|
"or repeating that the app is 'free' and 'self-hostable'."
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
@ -342,7 +338,8 @@ def add_to_wishlist():
|
||||||
csrf_token=csrf_token,
|
csrf_token=csrf_token,
|
||||||
successmsg=None,
|
successmsg=None,
|
||||||
errormsg=_(
|
errormsg=_(
|
||||||
"An entry with the name %(slug)s already exists in the wishlist, instead, you can <a href='%(url)s'>add a star to the app to show your interest</a>.",
|
"An entry with the name %(slug)s already exists in the wishlist, instead, "
|
||||||
|
"you can <a href='%(url)s'>add a star to the app to show your interest</a>.",
|
||||||
slug=slug,
|
slug=slug,
|
||||||
url=url,
|
url=url,
|
||||||
),
|
),
|
||||||
|
@ -362,12 +359,13 @@ def add_to_wishlist():
|
||||||
# Get the commit base for the new branch, and create it
|
# Get the commit base for the new branch, and create it
|
||||||
commit_sha = repo.get_branch(repo.default_branch).commit.sha
|
commit_sha = repo.get_branch(repo.default_branch).commit.sha
|
||||||
repo.create_git_ref(ref=f"refs/heads/{new_branch}", sha=commit_sha)
|
repo.create_git_ref(ref=f"refs/heads/{new_branch}", sha=commit_sha)
|
||||||
except exception as e:
|
except Exception as e:
|
||||||
print("… Failed to create branch ?")
|
print("… Failed to create branch ?")
|
||||||
print(e)
|
print(e)
|
||||||
url = "https://github.com/YunoHost/apps/pulls?q=is%3Apr+is%3Aopen+wishlist"
|
url = "https://github.com/YunoHost/apps/pulls?q=is%3Apr+is%3Aopen+wishlist"
|
||||||
errormsg = _(
|
errormsg = _(
|
||||||
"Failed to create the pull request to add the app to the wishlist… Maybe there's already <a href='%(url)s'>a waiting PR for this app</a>? Else, please report the issue to the YunoHost team.",
|
"Failed to create the pull request to add the app to the wishlist… Maybe there's already "
|
||||||
|
"<a href='%(url)s'>a waiting PR for this app</a>? Else, please report the issue to the YunoHost team.",
|
||||||
url=url,
|
url=url,
|
||||||
)
|
)
|
||||||
return render_template(
|
return render_template(
|
||||||
|
@ -419,7 +417,8 @@ Description: {description}
|
||||||
url = f"https://github.com/YunoHost/apps/pull/{pr.number}"
|
url = f"https://github.com/YunoHost/apps/pull/{pr.number}"
|
||||||
|
|
||||||
successmsg = _(
|
successmsg = _(
|
||||||
"Your proposed app has succesfully been submitted. It must now be validated by the YunoHost team. You can track progress here: <a href='%(url)s'>%(url)s</a>",
|
"Your proposed app has succesfully been submitted. It must now be validated by the YunoHost team. "
|
||||||
|
"You can track progress here: <a href='%(url)s'>%(url)s</a>",
|
||||||
url=url,
|
url=url,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -495,7 +494,10 @@ def sso_login_callback():
|
||||||
_("Unfortunately, login was denied.")
|
_("Unfortunately, login was denied.")
|
||||||
+ "<br/><br/>"
|
+ "<br/><br/>"
|
||||||
+ _(
|
+ _(
|
||||||
"Note that, due to various abuses, we restricted login on the app store to 'trust level 1' users.<br/><br/>'Trust level 1' is obtained after interacting a minimum with the forum, and more specifically: entering at least 5 topics, reading at least 30 posts, and spending at least 10 minutes reading posts."
|
"Note that, due to various abuses, we restricted login on the app store to 'trust level 1' users."
|
||||||
|
"<br/><br/>'Trust level 1' is obtained after interacting a minimum with the forum, and more "
|
||||||
|
"specifically: entering at least 5 topics, reading at least 30 posts, and spending at least "
|
||||||
|
"10 minutes reading posts."
|
||||||
),
|
),
|
||||||
403,
|
403,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
import os
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
install_dir = os.path.dirname(__file__)
|
from pathlib import Path
|
||||||
|
|
||||||
|
install_dir = Path(__file__).resolve().parent
|
||||||
command = f"{install_dir}/venv/bin/gunicorn"
|
command = f"{install_dir}/venv/bin/gunicorn"
|
||||||
pythonpath = install_dir
|
pythonpath = str(install_dir)
|
||||||
workers = 4
|
workers = 4
|
||||||
user = "appstore"
|
user = "appstore"
|
||||||
bind = f"unix:{install_dir}/sock"
|
bind = f"unix:{install_dir}/sock"
|
||||||
pid = "/run/gunicorn/appstore-pid"
|
pid = "/run/gunicorn/appstore-pid"
|
||||||
errorlog = "/var/log/appstore/error.log"
|
errorlog = "/var/log/appstore/error.log"
|
||||||
accesslog = "/var/log/appstore/access.log"
|
accesslog = "/var/log/appstore/access.log"
|
||||||
access_log_format = '%({X-Real-IP}i)s %({X-Forwarded-For}i)s %(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"'
|
access_log_format = \
|
||||||
|
'%({X-Real-IP}i)s %({X-Forwarded-For}i)s %(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"'
|
||||||
loglevel = "warning"
|
loglevel = "warning"
|
||||||
capture_output = True
|
capture_output = True
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
Flask==2.3.2
|
|
||||||
python-slugify
|
|
||||||
PyGithub
|
|
||||||
toml
|
|
||||||
pycmarkgfm
|
|
||||||
gunicorn
|
|
||||||
emoji
|
|
||||||
Babel
|
|
||||||
Flask-Babel
|
|
|
@ -1,15 +1,22 @@
|
||||||
import time
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
import os
|
|
||||||
import json
|
import json
|
||||||
import toml
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import time
|
||||||
|
from hashlib import md5
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
import pycmarkgfm
|
import pycmarkgfm
|
||||||
|
import tomlkit
|
||||||
from emoji import emojize
|
from emoji import emojize
|
||||||
from flask import request
|
from flask import request
|
||||||
from hashlib import md5
|
|
||||||
|
|
||||||
AVAILABLE_LANGUAGES = ["en"] + os.listdir("translations")
|
TRANSLATIONS_DIR = Path(__file__).parent / "translations"
|
||||||
|
|
||||||
|
|
||||||
|
AVAILABLE_LANGUAGES = ["en"] + [str(d) for d in TRANSLATIONS_DIR.glob("*/")]
|
||||||
|
|
||||||
|
|
||||||
def get_locale():
|
def get_locale():
|
||||||
|
@ -19,12 +26,13 @@ def get_locale():
|
||||||
|
|
||||||
|
|
||||||
def get_catalog():
|
def get_catalog():
|
||||||
path = "../builds/default/v3/apps.json"
|
path = Path("../builds/default/v3/apps.json").resolve()
|
||||||
mtime = os.path.getmtime(path)
|
|
||||||
|
mtime = path.stat().st_mtime
|
||||||
if get_catalog.mtime_catalog != mtime:
|
if get_catalog.mtime_catalog != mtime:
|
||||||
get_catalog.mtime_catalog = mtime
|
get_catalog.mtime_catalog = mtime
|
||||||
|
|
||||||
catalog = json.load(open(path))
|
catalog = json.load(path.open())
|
||||||
catalog["categories"] = {c["id"]: c for c in catalog["categories"]}
|
catalog["categories"] = {c["id"]: c for c in catalog["categories"]}
|
||||||
catalog["antifeatures"] = {c["id"]: c for c in catalog["antifeatures"]}
|
catalog["antifeatures"] = {c["id"]: c for c in catalog["antifeatures"]}
|
||||||
|
|
||||||
|
@ -58,11 +66,11 @@ get_catalog()
|
||||||
|
|
||||||
|
|
||||||
def get_wishlist():
|
def get_wishlist():
|
||||||
path = "../wishlist.toml"
|
path = Path("../wishlist.toml").resolve()
|
||||||
mtime = os.path.getmtime(path)
|
mtime = path.stat().st_mtime
|
||||||
if get_wishlist.mtime_wishlist != mtime:
|
if get_wishlist.mtime_wishlist != mtime:
|
||||||
get_wishlist.mtime_wishlist = mtime
|
get_wishlist.mtime_wishlist = mtime
|
||||||
get_wishlist.cache_wishlist = toml.load(open(path))
|
get_wishlist.cache_wishlist = tomlkit.load(path.open())
|
||||||
|
|
||||||
return get_wishlist.cache_wishlist
|
return get_wishlist.cache_wishlist
|
||||||
|
|
||||||
|
@ -96,26 +104,22 @@ get_stars()
|
||||||
|
|
||||||
def check_wishlist_submit_ratelimit(user):
|
def check_wishlist_submit_ratelimit(user):
|
||||||
|
|
||||||
dir_ = os.path.join(".wishlist_ratelimit")
|
dir_ = Path(".wishlist_ratelimit").resolve()
|
||||||
if not os.path.exists(dir_):
|
dir_.mkdir(exist_ok=True)
|
||||||
os.mkdir(dir_)
|
f = dir_ / md5(user.encode()).hexdigest()
|
||||||
|
|
||||||
f = os.path.join(dir_, md5(user.encode()).hexdigest())
|
return not f.exists() or (time.time() - f.stat().st_mtime) > (
|
||||||
|
|
||||||
return not os.path.exists(f) or (time.time() - os.path.getmtime(f)) > (
|
|
||||||
15 * 24 * 3600
|
15 * 24 * 3600
|
||||||
) # 15 days
|
) # 15 days
|
||||||
|
|
||||||
|
|
||||||
def save_wishlist_submit_for_ratelimit(user):
|
def save_wishlist_submit_for_ratelimit(user):
|
||||||
|
|
||||||
dir_ = os.path.join(".wishlist_ratelimit")
|
dir_ = Path(".wishlist_ratelimit").resolve()
|
||||||
if not os.path.exists(dir_):
|
dir_.mkdir(exist_ok=True)
|
||||||
os.mkdir(dir_)
|
|
||||||
|
|
||||||
f = os.path.join(dir_, md5(user.encode()).hexdigest())
|
f = dir_ / md5(user.encode()).hexdigest()
|
||||||
|
f.touch()
|
||||||
open(f, "w").write("")
|
|
||||||
|
|
||||||
|
|
||||||
def human_to_binary(size: str) -> int:
|
def human_to_binary(size: str) -> int:
|
||||||
|
@ -133,7 +137,7 @@ def human_to_binary(size: str) -> int:
|
||||||
try:
|
try:
|
||||||
size_ = float(size)
|
size_ = float(size)
|
||||||
except Exception:
|
except Exception:
|
||||||
raise Exception(f"Failed to convert size {size} to float")
|
raise Exception(f"Failed to convert size {size} to float") # noqa: B904
|
||||||
|
|
||||||
return int(size_ * factor[suffix])
|
return int(size_ * factor[suffix])
|
||||||
|
|
||||||
|
@ -141,46 +145,48 @@ def human_to_binary(size: str) -> int:
|
||||||
def get_app_md_and_screenshots(app_folder, infos):
|
def get_app_md_and_screenshots(app_folder, infos):
|
||||||
locale = get_locale()
|
locale = get_locale()
|
||||||
|
|
||||||
if locale != "en" and os.path.exists(
|
description_path_localized = app_folder / "doc" / f"DESCRIPTION_{locale}.md"
|
||||||
os.path.join(app_folder, "doc", f"DESCRIPTION_{locale}.md")
|
description_path_generic = app_folder / "doc" / "DESCRIPTION.md"
|
||||||
):
|
|
||||||
description_path = os.path.join(app_folder, "doc", f"DESCRIPTION_{locale}.md")
|
if locale != "en" and description_path_localized.exists():
|
||||||
elif os.path.exists(os.path.join(app_folder, "doc", "DESCRIPTION.md")):
|
description_path = description_path_localized
|
||||||
description_path = os.path.join(app_folder, "doc", "DESCRIPTION.md")
|
elif description_path_generic.exists():
|
||||||
|
description_path = description_path_generic
|
||||||
else:
|
else:
|
||||||
description_path = None
|
description_path = None
|
||||||
if description_path:
|
if description_path:
|
||||||
with open(description_path) as f:
|
with description_path.open() as f:
|
||||||
infos["full_description_html"] = emojize(
|
infos["full_description_html"] = emojize(
|
||||||
pycmarkgfm.gfm_to_html(f.read()), language="alias"
|
pycmarkgfm.gfm_to_html(f.read()), language="alias"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
infos["full_description_html"] = infos["manifest"]["description"][locale]
|
infos["full_description_html"] = infos["manifest"]["description"][locale]
|
||||||
|
|
||||||
if locale != "en" and os.path.exists(
|
preinstall_path_localized = app_folder / "doc" / f"PRE_INSTALL_{locale}.md"
|
||||||
os.path.join(app_folder, "doc", f"PRE_INSTALL_{locale}.md")
|
preinstall_path_generic = app_folder / "doc" / "PRE_INSTALL.md"
|
||||||
):
|
|
||||||
pre_install_path = os.path.join(app_folder, "doc", f"PRE_INSTALL_{locale}.md")
|
if locale != "en" and preinstall_path_localized.exists():
|
||||||
elif os.path.exists(os.path.join(app_folder, "doc", "PRE_INSTALL.md")):
|
pre_install_path = preinstall_path_localized
|
||||||
pre_install_path = os.path.join(app_folder, "doc", "PRE_INSTALL.md")
|
elif preinstall_path_generic.exists():
|
||||||
|
pre_install_path = preinstall_path_generic
|
||||||
else:
|
else:
|
||||||
pre_install_path = None
|
pre_install_path = None
|
||||||
if pre_install_path:
|
if pre_install_path:
|
||||||
with open(pre_install_path) as f:
|
with pre_install_path.open() as f:
|
||||||
infos["pre_install_html"] = emojize(
|
infos["pre_install_html"] = emojize(
|
||||||
pycmarkgfm.gfm_to_html(f.read()), language="alias"
|
pycmarkgfm.gfm_to_html(f.read()), language="alias"
|
||||||
)
|
)
|
||||||
|
|
||||||
infos["screenshot"] = None
|
infos["screenshot"] = None
|
||||||
|
|
||||||
screenshots_folder = os.path.join(app_folder, "doc", "screenshots")
|
screenshots_folder = app_folder / "doc" / "screenshots"
|
||||||
|
|
||||||
if os.path.exists(screenshots_folder):
|
if screenshots_folder.exists():
|
||||||
with os.scandir(screenshots_folder) as it:
|
with screenshots_folder.iterdir() as it:
|
||||||
for entry in it:
|
for entry in it:
|
||||||
ext = os.path.splitext(entry.name)[1].replace(".", "").lower()
|
ext = entry.suffix.lower()
|
||||||
if entry.is_file() and ext in ("png", "jpg", "jpeg", "webp", "gif"):
|
if entry.is_file() and ext in ("png", "jpg", "jpeg", "webp", "gif"):
|
||||||
with open(entry.path, "rb") as img_file:
|
with entry.open("rb") as img_file:
|
||||||
data = base64.b64encode(img_file.read()).decode("utf-8")
|
data = base64.b64encode(img_file.read()).decode("utf-8")
|
||||||
infos["screenshot"] = (
|
infos["screenshot"] = (
|
||||||
f"data:image/{ext};charset=utf-8;base64,{data}"
|
f"data:image/{ext};charset=utf-8;base64,{data}"
|
||||||
|
|
Loading…
Add table
Reference in a new issue