from PIL import Image, ImageOps, ImageDraw, ImageChops from io import BytesIO from glob import glob import numpy as np import base64 import os def base64_to_img(base64_bytes: bytes) -> Image: img_bytes = base64.b64decode(base64_bytes) img_buffer = BytesIO(img_bytes) img = Image.open(img_buffer) img = img.convert("RGBA") return img def img_to_array(img: Image) -> np.array: img_array = np.array(img) return img_array def _avg_colors(img: Image) -> tuple: img_array = img_to_array(img) avg_colors = np.mean(img_array, axis=tuple(range(img_array.ndim - 1))) avg_list = list(map(int, avg_colors)) avg_list[3] = 255 return tuple(avg_list) def __avg_colors(pil_img): img = pil_img.copy() img.convert("RGB") img.resize((1, 2), resample=0) dominant_color = img.getpixel((0, 0)) print(img.getpixel((0, 1))) return dominant_color def avg_colors(img): img_array = img_to_array(img) colors, count = np.unique(img_array.reshape(-1,img_array.shape[-1]), axis=0, return_counts=True) return tuple(colors[count.argmax()]) def add_background(img: Image, bg_tuple: tuple) -> Image: bg = Image.new("RGBA", img.size, bg_tuple) bg.paste(img, (0, 0), img) return bg def crop_to_circle(im, template): im = im.copy() bigsize = (im.size[0] * 3, im.size[1] * 3) mask = Image.new('L', bigsize, 0) ImageDraw.Draw(mask).ellipse((0, 0) + bigsize, fill=255) midsize = tuple(x//2 for x in bigsize) if template[0] == 1: ImageDraw.Draw(mask).rectangle((0, 0) + midsize, fill=255) if template[1] == 1: ImageDraw.Draw(mask).rectangle((midsize[0], 0, bigsize[0], midsize[1]), fill=255) if template[2] == 1: ImageDraw.Draw(mask).rectangle((0, midsize[1], midsize[1], bigsize[1]), fill=255) if template[3] == 1: ImageDraw.Draw(mask).rectangle(midsize + bigsize, fill=255) mask = mask.resize(im.size, Image.ANTIALIAS) mask = ImageChops.darker(mask, im.split()[-1]) im.putalpha(mask) return im def parse_file(desktop_path): output = {} with open(desktop_path, "r") as f: for line in f: if not line: continue if line.startswith("["): continue key, value = line.split("=", 1) output[key] = value.strip() output["path"] = desktop_path return output def deparse_file(app): output = ["[Desktop Entry]"] for key, value in app.items(): output.append(f"{key}={value}") return "\n".join(output) def get_web_icons(): path = os.path.join( os.path.expanduser("~"), ".local", "share", "applications", "sailfish-browser*.desktop" ) return glob(path) def sailify(raw_str, pattern): base64_bytes = raw_str.replace("data:image/png;base64,", "", 1).encode() img = base64_to_img(base64_bytes) bg_tuple = avg_colors(img) img = add_background(img, bg_tuple) img = crop_to_circle(img, pattern) output_buffer = BytesIO() img.save(output_buffer, format="PNG") output_data = output_buffer.getvalue() output_base64 = base64.b64encode(output_data) return "data:image/png;base64," + output_base64.decode() def main(): import shutil while True: apps = get_web_icons() apps = list(map(parse_file, apps)) 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] 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()