diff --git a/dependencies.txt b/dependencies.txt index 46c18f7..eb44887 100644 --- a/dependencies.txt +++ b/dependencies.txt @@ -1,5 +1,7 @@ libsailfishapp-launcher +sailfish-svg2png pyotherside-qml-plugin-python3-qt5 python3-base python3-imaging python3-requests +python3-lxml diff --git a/src/main.py b/src/main.py index b2058da..782045c 100644 --- a/src/main.py +++ b/src/main.py @@ -4,34 +4,59 @@ from PIL.Image import Image as ImageType from io import BytesIO from glob import glob from collections import Counter +from subprocess import getstatusoutput +from urllib.parse import urlparse +from functools import lru_cache import requests import base64 import os import shutil import logging +import tempfile + +ICON_SIZE = (256, 256) logging_filename = os.path.join(os.path.expanduser("~"), ".local", "share", - "org.kirbylife", + "dev.kirbylife", "harbour-muchkin", "muchkin.log") os.makedirs(os.path.dirname(logging_filename), exist_ok=True) logging.basicConfig(filename=logging_filename, filemode="w", level=logging.DEBUG) +@lru_cache(maxsize=50) def download_icon(url) -> ImageType: response = requests.get(url) img = Image.open(BytesIO(response.content)) + img.thumbnail(ICON_SIZE) img = img.convert("RGBA") return img +@lru_cache(maxsize=50) def base64_to_img(base64_bytes: bytes) -> ImageType: img_bytes = base64.b64decode(base64_bytes) img_buffer = BytesIO(img_bytes) img = Image.open(img_buffer) + img.thumbnail(ICON_SIZE) img = img.convert("RGBA") return img +@lru_cache(maxsize=50) +def svg_to_img(svg_url: str) -> ImageType: + response = requests.get(svg_url) + with tempfile.TemporaryDirectory() as d: + with open(os.path.join(d, "icon.svg"), "w") as f: + f.write(response.text) + status, output = getstatusoutput(f"sailfish_svg2png -z 16 {d} {d}") + logging.info("Converted svg to png") + logging.info(f"sailfish_svg2png status {status}: {output}") + + img = Image.open(os.path.join(d, "icon.png")) + img.thumbnail(ICON_SIZE) + img = img.convert("RGBA") + return img + def avg_colors(img): counter = Counter() width, height = img.size @@ -42,9 +67,12 @@ def avg_colors(img): counter[pixel] += 1 return counter.most_common(1)[0][0] -def add_background(img: ImageType, bg_tuple: tuple) -> ImageType: +def add_background(img: ImageType, bg_tuple: tuple, size: float) -> ImageType: + img = img.copy() + size = size / 100 bg = Image.new("RGBA", img.size, bg_tuple) - bg.paste(img, (0, 0), img) + img.thumbnail((value * size for value in img.size)) + bg.paste(img, ((bg.size[0] - img.size[0]) // 2, (bg.size[1] - img.size[1]) // 2), img) return bg def crop_to_circle(im, template): @@ -70,9 +98,7 @@ def parse_file(desktop_path): output = {} with open(desktop_path, "r") as f: for line in f: - if not line: - continue - if line.startswith("["): + if not line or line.startswith("["): continue key, value = line.split("=", 1) output[key] = value.strip() @@ -97,21 +123,23 @@ def get_web_icons(): return list(map(parse_file, apps)) -def sailify(raw_str, pattern): - try: - # New versions of Sailfish has the url on the icons - img = download_icon(raw_str) - logging.info("Downloaded from url: " + raw_str) - except requests.exceptions.InvalidSchema: +def sailify(raw_str, pattern, size) -> str: + url_parsed = urlparse(raw_str) + if url_parsed.scheme == "data": # The old ones converted the icon to base64 base64_bytes = raw_str.replace("data:image/png;base64,", "", 1).encode() img = base64_to_img(base64_bytes) logging.info("Converted from base64") - except Exception as e: - logging.error(e) - raise e + # New versions of Sailfish has the url on the icons + elif url_parsed.path.split("/")[-1].split(".")[-1].upper() == "SVG": + img = svg_to_img(raw_str) + logging.info("Downloaded from url: " + raw_str) + else: + img = download_icon(raw_str) + logging.info("Downloaded from url: " + raw_str) + bg_tuple = avg_colors(img) - img = add_background(img, bg_tuple) + img = add_background(img, bg_tuple, size) img = crop_to_circle(img, pattern) output_buffer = BytesIO() img.save(output_buffer, format="PNG") @@ -133,43 +161,3 @@ def save_icon(app): f.write(new_content) logging.info("Icon saved on: " + app["path"]) return True - -def main(): - import shutil - - while True: - apps = get_web_icons() - while True: - for n, app in enumerate(apps): - print(f"{n}) {app['Name']}") - index = input("select an application (the index):\n>>> ") - index = int(index) - if index >= len(apps): - print("Select a valid index") - continue - break - app = apps[index] - - if app["Icon"] == "icon-launcher-bookmark": - print("this webapp is using the generic icon of a bookmark and not a favicon so it is not possible to modify") - continue - - while True: - pattern = input("Enter the pattern (0 = round, 1 = peak):\n>>> ") - if len(pattern) != 4: - print("it is necessary to enter 4 characters") - pattern = tuple(map(int, pattern)) - new_icon = sailify(app.get("old_icon", app["Icon"]), pattern) - # Backup the original .desktop - shutil.copyfile(app["path"], app["path"] + "_backup") - app["old_icon"] = app.get("old_icon", app["Icon"]) - app["Icon"] = new_icon - new_content = deparse_file(app) - with open(app["path"], "w") as f: - f.write(new_content) - print(f"{app['Name']} sailified correctly") - break - - -if __name__ == "__main__": - main()