mirror of
https://github.com/YunoHost/apps.git
synced 2024-09-03 20:06:07 +02:00
199 lines
5.7 KiB
Python
199 lines
5.7 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import base64
|
|
import json
|
|
import os
|
|
import subprocess
|
|
import time
|
|
from hashlib import md5
|
|
from pathlib import Path
|
|
|
|
import pycmarkgfm
|
|
import tomlkit
|
|
from emoji import emojize
|
|
from flask import request
|
|
|
|
TRANSLATIONS_DIR = Path(__file__).parent / "translations"
|
|
|
|
|
|
AVAILABLE_LANGUAGES = ["en"] + [str(d) for d in TRANSLATIONS_DIR.glob("*/")]
|
|
|
|
|
|
def get_locale():
|
|
# try to guess the language from the user accept
|
|
# The best match wins.
|
|
return request.accept_languages.best_match(AVAILABLE_LANGUAGES) or "en"
|
|
|
|
|
|
def get_catalog():
|
|
path = Path("../builds/default/v3/apps.json").resolve()
|
|
|
|
mtime = path.stat().st_mtime
|
|
if get_catalog.mtime_catalog != mtime:
|
|
get_catalog.mtime_catalog = mtime
|
|
|
|
catalog = json.load(path.open())
|
|
catalog["categories"] = {c["id"]: c for c in catalog["categories"]}
|
|
catalog["antifeatures"] = {c["id"]: c for c in catalog["antifeatures"]}
|
|
|
|
category_color = {
|
|
"synchronization": "sky",
|
|
"publishing": "yellow",
|
|
"communication": "amber",
|
|
"office": "lime",
|
|
"productivity_and_management": "purple",
|
|
"small_utilities": "black",
|
|
"reading": "emerald",
|
|
"multimedia": "fuchsia",
|
|
"social_media": "rose",
|
|
"games": "violet",
|
|
"dev": "stone",
|
|
"system_tools": "black",
|
|
"iot": "orange",
|
|
"wat": "teal",
|
|
}
|
|
|
|
for id_, category in catalog["categories"].items():
|
|
category["color"] = category_color[id_]
|
|
|
|
get_catalog.cache_catalog = catalog
|
|
|
|
return get_catalog.cache_catalog
|
|
|
|
|
|
get_catalog.mtime_catalog = None
|
|
get_catalog()
|
|
|
|
|
|
def get_wishlist():
|
|
path = Path("../wishlist.toml").resolve()
|
|
mtime = path.stat().st_mtime
|
|
if get_wishlist.mtime_wishlist != mtime:
|
|
get_wishlist.mtime_wishlist = mtime
|
|
get_wishlist.cache_wishlist = tomlkit.load(path.open())
|
|
|
|
return get_wishlist.cache_wishlist
|
|
|
|
|
|
get_wishlist.mtime_wishlist = None
|
|
get_wishlist()
|
|
|
|
|
|
def get_stars():
|
|
checksum = (
|
|
subprocess.check_output("find . -type f -printf '%T@,' | md5sum", shell=True)
|
|
.decode()
|
|
.split()[0]
|
|
)
|
|
if get_stars.cache_checksum != checksum:
|
|
stars = {}
|
|
for folder, _, files in os.walk(".stars/"):
|
|
app_id = folder.split("/")[-1]
|
|
if not app_id:
|
|
continue
|
|
stars[app_id] = set(files)
|
|
get_stars.cache_stars = stars
|
|
get_stars.cache_checksum = checksum
|
|
|
|
return get_stars.cache_stars
|
|
|
|
|
|
get_stars.cache_checksum = None
|
|
get_stars()
|
|
|
|
|
|
def check_wishlist_submit_ratelimit(user):
|
|
|
|
dir_ = Path(".wishlist_ratelimit").resolve()
|
|
dir_.mkdir(exist_ok=True)
|
|
f = dir_ / md5(user.encode()).hexdigest()
|
|
|
|
return not f.exists() or (time.time() - f.stat().st_mtime) > (
|
|
15 * 24 * 3600
|
|
) # 15 days
|
|
|
|
|
|
def save_wishlist_submit_for_ratelimit(user):
|
|
|
|
dir_ = Path(".wishlist_ratelimit").resolve()
|
|
dir_.mkdir(exist_ok=True)
|
|
|
|
f = dir_ / md5(user.encode()).hexdigest()
|
|
f.touch()
|
|
|
|
|
|
def human_to_binary(size: str) -> int:
|
|
symbols = ("K", "M", "G", "T", "P", "E", "Z", "Y")
|
|
factor = {}
|
|
for i, s in enumerate(symbols):
|
|
factor[s] = 1 << (i + 1) * 10
|
|
|
|
suffix = size[-1]
|
|
size = size[:-1]
|
|
|
|
if suffix not in symbols:
|
|
raise Exception(f"Invalid size suffix '{suffix}', expected one of {symbols}")
|
|
|
|
try:
|
|
size_ = float(size)
|
|
except Exception:
|
|
raise Exception(f"Failed to convert size {size} to float") # noqa: B904
|
|
|
|
return int(size_ * factor[suffix])
|
|
|
|
|
|
def get_app_md_and_screenshots(app_folder, infos):
|
|
locale = get_locale()
|
|
|
|
description_path_localized = app_folder / "doc" / f"DESCRIPTION_{locale}.md"
|
|
description_path_generic = app_folder / "doc" / "DESCRIPTION.md"
|
|
|
|
if locale != "en" and description_path_localized.exists():
|
|
description_path = description_path_localized
|
|
elif description_path_generic.exists():
|
|
description_path = description_path_generic
|
|
else:
|
|
description_path = None
|
|
if description_path:
|
|
with description_path.open() as f:
|
|
infos["full_description_html"] = emojize(
|
|
pycmarkgfm.gfm_to_html(f.read()), language="alias"
|
|
)
|
|
else:
|
|
infos["full_description_html"] = infos["manifest"]["description"][locale]
|
|
|
|
preinstall_path_localized = app_folder / "doc" / f"PRE_INSTALL_{locale}.md"
|
|
preinstall_path_generic = app_folder / "doc" / "PRE_INSTALL.md"
|
|
|
|
if locale != "en" and preinstall_path_localized.exists():
|
|
pre_install_path = preinstall_path_localized
|
|
elif preinstall_path_generic.exists():
|
|
pre_install_path = preinstall_path_generic
|
|
else:
|
|
pre_install_path = None
|
|
if pre_install_path:
|
|
with pre_install_path.open() as f:
|
|
infos["pre_install_html"] = emojize(
|
|
pycmarkgfm.gfm_to_html(f.read()), language="alias"
|
|
)
|
|
|
|
infos["screenshot"] = None
|
|
|
|
screenshots_folder = app_folder / "doc" / "screenshots"
|
|
|
|
if screenshots_folder.exists():
|
|
with screenshots_folder.iterdir() as it:
|
|
for entry in it:
|
|
ext = entry.suffix.lower()
|
|
if entry.is_file() and ext in ("png", "jpg", "jpeg", "webp", "gif"):
|
|
with entry.open("rb") as img_file:
|
|
data = base64.b64encode(img_file.read()).decode("utf-8")
|
|
infos["screenshot"] = (
|
|
f"data:image/{ext};charset=utf-8;base64,{data}"
|
|
)
|
|
break
|
|
|
|
ram_build_requirement = infos["manifest"]["integration"]["ram"]["build"]
|
|
infos["manifest"]["integration"]["ram"]["build_binary"] = human_to_binary(
|
|
ram_build_requirement
|
|
)
|