Add the option to search a better icon on the html page

master
kirbylife 2024-04-04 01:14:25 -06:00
parent 33ebb4bdd4
commit 4c9b7da927
4 changed files with 197 additions and 91 deletions

View File

@ -1,2 +1,17 @@
*Mon Apr 27 2015 Tobi Sim 0.0.1-1 *Mon Feb 13 2023 kirbylife <hola@kirbylife.dev> 0.5.0-7
-initial commit - Add SVG support
- Improve performance
- Add resize slider
- Fix irregluar size of icons on the main grid
- Add the option to search a better icon directly on the html page
- Improve a bit the logging
*Mon Feb 13 2023 kirbylife <hola@kirbylife.dev> 0.4.5-5
- Add aarch64 support
- Add logging file
- Skip the sailjail permissions
- Remove all the third-repo dependencies
- Now, when you re-edit an icon, the original icon is used instead of the edited one
*Mon May 17 2021 kirbylife <hola@kirbylife.dev> 0.4.0-1
- First published version

View File

@ -1,6 +1,7 @@
import QtQuick 2.0 import QtQuick 2.0
import Sailfish.Silica 1.0 import Sailfish.Silica 1.0
import io.thp.pyotherside 1.3 import io.thp.pyotherside 1.3
import Nemo.Notifications 1.0
Page { Page {
id: iconEditor id: iconEditor
@ -8,6 +9,40 @@ Page {
property var attrs property var attrs
property var corners: [true, true, true, true] property var corners: [true, true, true, true]
Notification {
id: notification
appIcon: "/usr/share/icons/hicolor/86x86/apps/harbour-muchkin"
expireTimeout: 5000
function sendMessage(msg) {
notification.previewBody = msg
notification.publish()
}
}
SilicaFlickable {
id: mainList
anchors.fill: parent
PullDownMenu {
MenuItem {
text: "Search a better icon"
onClicked: {
py.importModule("main", function(){
py.call("main.scrape_icon", [iconEditor.attrs.URL], function(result){
if(result) {
icon.source = result;
iconEditor.attrs.Icon = result;
notification.sendMessage("A better icon found");
} else {
notification.sendMessage("Not better icon found");
}
})
})
}
}
}
PageHeader { PageHeader {
id: header id: header
width: parent.width width: parent.width
@ -115,4 +150,7 @@ Page {
}) })
} }
} }
}
} }

13
src/decorators.py 100644
View File

@ -0,0 +1,13 @@
import logging
def log_error(f):
def wrap(*args, **kwargs):
try:
return f(*args, **kwargs)
except Exception as e:
logging.error(f"Error on: {f.__name__}")
logging.error(f"args: {args}")
logging.error(f"kwargs: {kwargs}")
logging.error(f"Exception: {e}")
raise e
return wrap

View File

@ -1,4 +1,5 @@
import pyotherside # pyright: ignore reportMissingImports import pyotherside # pyright: ignore reportMissingImports
from decorators import log_error
from PIL import Image, ImageDraw, ImageChops from PIL import Image, ImageDraw, ImageChops
from PIL.Image import Image as ImageType from PIL.Image import Image as ImageType
from io import BytesIO from io import BytesIO
@ -6,7 +7,9 @@ from glob import glob
from collections import Counter from collections import Counter
from subprocess import getstatusoutput from subprocess import getstatusoutput
from urllib.parse import urlparse from urllib.parse import urlparse
from urllib.parse import urljoin
from functools import lru_cache from functools import lru_cache
from lxml import etree
import requests import requests
import base64 import base64
import os import os
@ -16,6 +19,10 @@ import tempfile
ICON_SIZE = (256, 256) ICON_SIZE = (256, 256)
HEADERS = {
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:124.0) Gecko/20100101 Firefox/124.0"
}
logging_filename = os.path.join(os.path.expanduser("~"), logging_filename = os.path.join(os.path.expanduser("~"),
".local", ".local",
"share", "share",
@ -69,7 +76,7 @@ def avg_colors(img):
def add_background(img: ImageType, bg_tuple: tuple, size: float) -> ImageType: def add_background(img: ImageType, bg_tuple: tuple, size: float) -> ImageType:
img = img.copy() img = img.copy()
size = size / 100 size = max(size, 1) / 100
bg = Image.new("RGBA", img.size, bg_tuple) bg = Image.new("RGBA", img.size, bg_tuple)
img.thumbnail((value * size for value in img.size)) 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) bg.paste(img, ((bg.size[0] - img.size[0]) // 2, (bg.size[1] - img.size[1]) // 2), img)
@ -123,6 +130,39 @@ def get_web_icons():
return list(map(parse_file, apps)) return list(map(parse_file, apps))
@log_error
def scrape_icon(url: str) -> str:
url_parsed = urlparse(url)
origin = f"{url_parsed.scheme}://{url_parsed.netloc}"
html = etree.HTML(requests.get(origin, headers=HEADERS).text)
possible_svg = None
possible_icon = None
max_size = 0
for icon in html.xpath("//link[contains(@rel, 'icon')]"):
if "SVG" in next(iter(icon.xpath("@type")), "").upper():
possible_svg = icon.xpath("@href")[0]
break
possible_href = next(iter(icon.xpath("@href")), None)
possible_size = next(iter(icon.xpath("@sizes")), None)
possible_rel = next(iter(icon.xpath("@rel")), "")
if possible_size and possible_href and possible_size != "any":
size = int(possible_size.split("x")[0])
if size > max_size:
max_size = size
possible_icon = possible_href
if possible_href and not possible_size and possible_rel.upper() == "apple-touch-icon".upper():
possible_icon = possible_href
new_icon = possible_svg or possible_icon
if new_icon:
parsed_new_icon = urlparse(new_icon)
if not parsed_new_icon.scheme or not parsed_new_icon.netloc:
new_icon = urljoin(origin, new_icon)
return sailify(new_icon, [True] * 4, 100)
@log_error
def sailify(raw_str, pattern, size) -> str: def sailify(raw_str, pattern, size) -> str:
url_parsed = urlparse(raw_str) url_parsed = urlparse(raw_str)
if url_parsed.scheme == "data": if url_parsed.scheme == "data":