Icon generation improvements

This commit is contained in:
itdominator 2023-02-08 17:51:42 -06:00
parent 9ae0182928
commit 51e48c57c3
2 changed files with 127 additions and 77 deletions

View File

@ -1,6 +1,8 @@
# Python imports # Python imports
import hashlib import os
from os.path import isfile from os.path import isfile
import hashlib
import threading
# Lib imports # Lib imports
import gi import gi
@ -11,7 +13,7 @@ from gi.repository import GdkPixbuf
try: try:
from PIL import Image as PImage from PIL import Image as PImage
except Exception as e: except ModuleNotFoundError as e:
PImage = None PImage = None
# Application imports # Application imports
@ -21,6 +23,10 @@ from .mixins.desktopiconmixin import DesktopIconMixin
class IconException(Exception):
...
class Icon(DesktopIconMixin, VideoIconMixin, MeshsIconMixin): class Icon(DesktopIconMixin, VideoIconMixin, MeshsIconMixin):
def create_icon(self, dir, file): def create_icon(self, dir, file):
@ -29,54 +35,53 @@ class Icon(DesktopIconMixin, VideoIconMixin, MeshsIconMixin):
def get_icon_image(self, dir, file, full_path): def get_icon_image(self, dir, file, full_path):
try: try:
thumbnl = None thumbnl = self._get_system_thumbnail_gtk_thread(full_path, self.sys_icon_wh[0])
if file.lower().endswith(self.fmeshs): # 3D Mesh icon if file.lower().endswith(self.fmeshs): # 3D Mesh icon
... ...
if file.lower().endswith(self.fvideos): # Video icon if file.lower().endswith(self.fvideos): # Video icon
thumbnl = self.create_thumbnail(dir, file, full_path) thumbnl = self.create_video_thumbnail(full_path)
elif file.lower().endswith(self.fimages): # Image Icon elif file.lower().endswith(self.fimages): # Image Icon
thumbnl = self.create_scaled_image(full_path) thumbnl = self.create_scaled_image(full_path)
elif file.lower().endswith( (".blend",) ): # Blender icon elif file.lower().endswith( (".blend",) ): # Blender icon
thumbnl = self.create_blender_thumbnail(dir, file, full_path) thumbnl = self.create_blender_thumbnail(full_path)
elif full_path.lower().endswith( ('.desktop',) ): # .desktop file parsing elif full_path.lower().endswith( ('.desktop',) ): # .desktop file parsing
thumbnl = self.parse_desktop_files(full_path) thumbnl = self.find_thumbnail_from_desktop_file(full_path)
if not thumbnl: if not thumbnl:
thumbnl = self.get_system_thumbnail(full_path, self.sys_icon_wh[0]) raise IconException("No known icons found.")
if not thumbnl:
thumbnl = self.return_generic_icon()
return thumbnl return thumbnl
except Exception: except IconException:
... ...
return self.return_generic_icon() return self.get_generic_icon()
def create_blender_thumbnail(self, dir, file, full_path=None): def create_blender_thumbnail(self, full_path):
try: try:
file_hash = hashlib.sha256(str.encode(full_path)).hexdigest() path_exists, hash_img_path = self.generate_hash_and_path(full_path)
hash_img_path = f"{self.ABS_THUMBS_PTH}/{file_hash}.png" if not path_exists:
if not isfile(hash_img_path):
self.generate_blender_thumbnail(full_path, hash_img_path) self.generate_blender_thumbnail(full_path, hash_img_path)
return self.create_scaled_image(hash_img_path, self.video_icon_wh) return self.create_scaled_image(hash_img_path, self.video_icon_wh)
except Exception as e: except IconException as e:
print("Blender thumbnail generation issue:") print("Blender thumbnail generation issue:")
print( repr(e) ) print( repr(e) )
return None return None
def create_thumbnail(self, dir, file, full_path=None, scrub_percent = "65%"): def create_video_thumbnail(self, full_path, scrub_percent = "65%", replace=False):
try: try:
file_hash = hashlib.sha256(str.encode(full_path)).hexdigest() path_exists, hash_img_path = self.generate_hash_and_path(full_path)
hash_img_path = f"{self.ABS_THUMBS_PTH}/{file_hash}.jpg" if path_exists and replace:
if not isfile(hash_img_path): os.remove(hash_img_path)
path_exists = False
if not path_exists:
self.generate_video_thumbnail(full_path, hash_img_path, scrub_percent) self.generate_video_thumbnail(full_path, hash_img_path, scrub_percent)
return self.create_scaled_image(hash_img_path, self.video_icon_wh) return self.create_scaled_image(hash_img_path, self.video_icon_wh)
except Exception as e: except IconException as e:
print("Image/Video thumbnail generation issue:") print("Image/Video thumbnail generation issue:")
print( repr(e) ) print( repr(e) )
@ -97,12 +102,77 @@ class Icon(DesktopIconMixin, VideoIconMixin, MeshsIconMixin):
return self.image2pixbuf(full_path, wxh) return self.image2pixbuf(full_path, wxh)
return GdkPixbuf.Pixbuf.new_from_file_at_scale(full_path, wxh[0], wxh[1], True) return GdkPixbuf.Pixbuf.new_from_file_at_scale(full_path, wxh[0], wxh[1], True)
except Exception as e: except IconException as e:
print("Image Scaling Issue:") print("Image Scaling Issue:")
print( repr(e) ) print( repr(e) )
return None return None
def create_from_file(self, full_path):
try:
return GdkPixbuf.Pixbuf.new_from_file(full_path)
except IconException as e:
print("Image from file Issue:")
print( repr(e) )
return None
def _get_system_thumbnail_gtk_thread(self, full_path, size):
def _call_gtk_thread(event, result):
result.append( self.get_system_thumbnail(full_path, size) )
event.set()
result = []
event = threading.Event()
GLib.idle_add(_call_gtk_thread, event, result)
event.wait()
return result[0]
def get_system_thumbnail(self, full_path, size):
try:
gio_file = Gio.File.new_for_path(full_path)
info = gio_file.query_info('standard::icon' , 0, None)
icon = info.get_icon().get_names()[0]
data = settings.get_icon_theme().lookup_icon(icon , size , 0)
if data:
icon_path = data.get_filename()
return GdkPixbuf.Pixbuf.new_from_file(icon_path)
raise IconException("No system icon found...")
except IconException:
...
return None
def get_generic_icon(self):
return GdkPixbuf.Pixbuf.new_from_file(self.DEFAULT_ICON)
def generate_hash_and_path(self, full_path):
file_hash = self.fast_hash(full_path)
hash_img_path = f"{self.ABS_THUMBS_PTH}/{file_hash}.jpg"
path_exists = True if isfile(hash_img_path) else False
return path_exists, hash_img_path
def fast_hash(self, filename, hash_factory=hashlib.md5, chunk_num_blocks=128, i=1):
h = hash_factory()
with open(filename,'rb') as f:
f.seek(0, 2)
mid = int(f.tell() / 2)
f.seek(mid, 0)
while chunk := f.read(chunk_num_blocks*h.block_size):
h.update(chunk)
if (i == 12):
break
i += 1
return h.hexdigest()
def image2pixbuf(self, full_path, wxh): def image2pixbuf(self, full_path, wxh):
"""Convert Pillow image to GdkPixbuf""" """Convert Pillow image to GdkPixbuf"""
im = PImage.open(full_path) im = PImage.open(full_path)
@ -114,28 +184,3 @@ class Icon(DesktopIconMixin, VideoIconMixin, MeshsIconMixin):
False, 8, w, h, w * 3) False, 8, w, h, w * 3)
return pixbuf.scale_simple(wxh[0], wxh[1], 2) # BILINEAR = 2 return pixbuf.scale_simple(wxh[0], wxh[1], 2) # BILINEAR = 2
def create_from_file(self, full_path):
try:
return GdkPixbuf.Pixbuf.new_from_file(full_path)
except Exception as e:
print("Image from file Issue:")
print( repr(e) )
return None
def get_system_thumbnail(self, filename, size):
try:
gio_file = Gio.File.new_for_path(filename)
info = gio_file.query_info('standard::icon' , 0, None)
icon = info.get_icon().get_names()[0]
icon_path = settings.get_icon_theme().lookup_icon(icon , size , 0).get_filename()
return GdkPixbuf.Pixbuf.new_from_file(icon_path)
except Exception:
...
return None
def return_generic_icon(self):
return GdkPixbuf.Pixbuf.new_from_file(self.DEFAULT_ICON)

View File

@ -7,7 +7,6 @@ import hashlib
# Lib imports # Lib imports
import gi import gi
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '3.0')
from gi.repository import Gtk from gi.repository import Gtk
from gi.repository import Gio from gi.repository import Gio
@ -18,21 +17,35 @@ from .xdg.DesktopEntry import DesktopEntry
class DesktopIconMixin: class DesktopIconMixin:
def parse_desktop_files(self, full_path): def find_thumbnail_from_desktop_file(self, full_path):
try: try:
xdgObj = DesktopEntry(full_path) xdgObj = DesktopEntry(full_path)
icon = xdgObj.getIcon() icon = xdgObj.getIcon()
alt_icon_path = "" alt_icon_path = ""
if "steam" in icon: if "steam" in icon:
return self.get_steam_img(xdgObj)
elif os.path.exists(icon):
return self.create_scaled_image(icon, self.sys_icon_wh)
else:
pixbuf, alt_icon_path = self.get_icon_from_traversal(icon)
if pixbuf:
return pixbuf
return self.create_scaled_image(alt_icon_path, self.sys_icon_wh)
except Exception as e:
print(".desktop icon generation issue:")
print( repr(e) )
return None
def get_steam_img(self, xdgObj):
name = xdgObj.getName() name = xdgObj.getName()
file_hash = hashlib.sha256(str.encode(name)).hexdigest() file_hash = hashlib.sha256(str.encode(name)).hexdigest()
hash_img_pth = f"{self.STEAM_ICONS_PTH}/{file_hash}.jpg" hash_img_pth = f"{self.STEAM_ICONS_PTH}/{file_hash}.jpg"
if isfile(hash_img_pth) == True: if not isfile(hash_img_pth):
# Use video sizes since headers are bigger
return self.create_scaled_image(hash_img_pth, self.video_icon_wh)
exec_str = xdgObj.getExec() exec_str = xdgObj.getExec()
parts = exec_str.split("steam://rungameid/") parts = exec_str.split("steam://rungameid/")
id = parts[len(parts) - 1] id = parts[len(parts) - 1]
@ -40,16 +53,12 @@ class DesktopIconMixin:
proc = subprocess.Popen(["wget", "-O", hash_img_pth, imageLink]) proc = subprocess.Popen(["wget", "-O", hash_img_pth, imageLink])
proc.wait() proc.wait()
# Use video thumbnail sizes since headers are bigger
return self.create_scaled_image(hash_img_pth, self.video_icon_wh) return self.create_scaled_image(hash_img_pth, self.video_icon_wh)
elif os.path.exists(icon):
return self.create_scaled_image(icon, self.sys_icon_wh) def get_icon_from_traversal(self, icon):
else:
gio_icon = Gio.Icon.new_for_string(icon) gio_icon = Gio.Icon.new_for_string(icon)
gicon = Gtk.Image.new_from_gicon(gio_icon, 32) gicon = Gtk.Image.new_from_gicon(gio_icon, 32)
pixbuf = gicon.get_pixbuf() pixbuf = gicon.get_pixbuf()
if pixbuf:
return pixbuf
alt_icon_path = "" alt_icon_path = ""
for dir in self.ICON_DIRS: for dir in self.ICON_DIRS:
@ -57,11 +66,7 @@ class DesktopIconMixin:
if alt_icon_path != "": if alt_icon_path != "":
break break
return self.create_scaled_image(alt_icon_path, self.sys_icon_wh) return pixbuf, alt_icon_path
except Exception as e:
print(".desktop icon generation issue:")
print( repr(e) )
return None
def traverse_icons_folder(self, path, icon): def traverse_icons_folder(self, path, icon):
for (dirpath, dirnames, filenames) in os.walk(path): for (dirpath, dirnames, filenames) in os.walk(path):