ShellFM/src/shellfm/windows/view/Icon.py

217 lines
8.5 KiB
Python

# Python Imports
import os, subprocess, threading, hashlib
from os.path import isfile, join
# Gtk imports
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
from gi.repository import Gtk
from gi.repository import Gio
from .utils import xdg
# Application imports
def threaded(fn):
def wrapper(*args, **kwargs):
threading.Thread(target=fn, args=args, kwargs=kwargs).start()
return wrapper
class Icon:
def create_icon(self, dir, file):
full_path = dir + "/" + file
return self.get_icon_image(dir, file, full_path)
def get_icon_image(self, dir, file, full_path):
try:
thumbnl = None
# Video icon
if file.lower().endswith(self.fvideos):
thumbnl = self.create_thumbnail(dir, file)
# thumbnl = Gtk.Image.new_from_file(self.DEFAULT_ICONS + "/video.png")
# Image Icon
elif file.lower().endswith(self.fimages):
thumbnl = self.create_scaled_image(full_path, self.VIDEO_ICON_WH)
# .desktop file parsing
elif full_path.lower().endswith( ('.desktop',) ):
thumbnl = self.parse_desktop_files(full_path)
# System icons
else:
thumbnl = self.get_system_thumbnail(full_path, self.SYS_ICON_WH[0])
if thumbnl == None: # If no icon whatsoever, return internal default
thumbnl = Gtk.Image.new_from_file(self.DEFAULT_ICON)
return thumbnl
except Exception as e:
print("Icon generation issue:")
print( repr(e) )
return Gtk.Image.new_from_file(self.DEFAULT_ICON)
def create_thumbnail(self, dir, file):
full_path = dir + "/" + file
try:
file_hash = hashlib.sha256(str.encode(full_path)).hexdigest()
hash_img_pth = self.ABS_THUMBS_PTH + "/" + file_hash + ".jpg"
if isfile(hash_img_pth) == False:
self.generate_video_thumbnail(full_path, hash_img_pth)
thumbnl = self.create_scaled_image(hash_img_pth, self.VIDEO_ICON_WH)
if thumbnl == None: # If no icon whatsoever, return internal default
thumbnl = Gtk.Image.new_from_file(self.DEFAULT_ICONS + "/video.png")
return thumbnl
except Exception as e:
print("Thumbnail generation issue:")
print( repr(e) )
return Gtk.Image.new_from_file(self.DEFAULT_ICONS + "/video.png")
def parse_desktop_files(self, full_path):
try:
xdgObj = xdg.DesktopEntry(full_path)
icon = xdgObj.getIcon()
alt_icon_path = ""
if "steam" in icon:
name = xdgObj.getName()
file_hash = hashlib.sha256(str.encode(name)).hexdigest()
hash_img_pth = self.STEAM_ICONS_PTH + "/" + file_hash + ".jpg"
if isfile(hash_img_pth) == True:
# Use video sizes since headers are bigger
return self.create_scaled_image(hash_img_pth, self.VIDEO_ICON_WH)
exec_str = xdgObj.getExec()
parts = exec_str.split("steam://rungameid/")
id = parts[len(parts) - 1]
imageLink = self.STEAM_BASE_URL + id + "/header.jpg"
proc = subprocess.Popen(["wget", "-O", hash_img_pth, imageLink])
proc.wait()
# Use video thumbnail sizes since headers are bigger
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)
else:
alt_icon_path = ""
for dir in self.ICON_DIRS:
alt_icon_path = self.traverse_icons_folder(dir, icon)
if alt_icon_path is not "":
break
return self.create_scaled_image(alt_icon_path, self.SYS_ICON_WH)
except Exception as e:
print(self.DEFAULT_ICON)
print(".desktop icon generation issue:")
print( repr(e) )
return None
def traverse_icons_folder(self, path, icon):
alt_icon_path = ""
for (dirpath, dirnames, filenames) in os.walk(path):
for file in filenames:
appNM = "application-x-" + icon
if icon in file or appNM in file:
alt_icon_path = dirpath + "/" + file
break
return alt_icon_path
def get_system_thumbnail(self, filename, size):
try:
if os.path.exists(filename):
gioFile = Gio.File.new_for_path(filename)
info = gioFile.query_info('standard::icon' , 0, Gio.Cancellable())
icon = info.get_icon().get_names()[0]
iconTheme = Gtk.IconTheme.get_default()
iconData = iconTheme.lookup_icon(icon , size , 0)
if iconData:
iconPath = iconData.get_filename()
return Gtk.Image.new_from_file(iconPath) # This seems to cause a lot of core dump issues...
else:
return None
else:
return None
except Exception as e:
print("system icon generation issue:")
print( repr(e) )
return None
def generate_video_thumbnail(self, full_path, hash_img_pth):
try:
proc = subprocess.Popen([self.FFMPG_THUMBNLR, "-t", "65%", "-s", "300", "-c", "jpg", "-i", full_path, "-o", hash_img_pth])
proc.wait()
except Exception as e:
self.logger.debug(repr(e))
self.ffprobe_generate_video_thumbnail(full_path, hash_img_pth)
def ffprobe_generate_video_thumbnail(self, full_path, hash_img_pth):
proc = None
try:
# Stream duration
command = ["ffprobe", "-v", "error", "-select_streams", "v:0", "-show_entries", "stream=duration", "-of", "default=noprint_wrappers=1:nokey=1", full_path]
data = subprocess.run(command, stdout=subprocess.PIPE)
duration = data.stdout.decode('utf-8')
# Format (container) duration
if "N/A" in duration:
command = ["ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", full_path]
data = subprocess.run(command , stdout=subprocess.PIPE)
duration = data.stdout.decode('utf-8')
# Stream duration type: image2
if "N/A" in duration:
command = ["ffprobe", "-v", "error", "-select_streams", "v:0", "-f", "image2", "-show_entries", "stream=duration", "-of", "default=noprint_wrappers=1:nokey=1", full_path]
data = subprocess.run(command, stdout=subprocess.PIPE)
duration = data.stdout.decode('utf-8')
# Format (container) duration type: image2
if "N/A" in duration:
command = ["ffprobe", "-v", "error", "-f", "image2", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", full_path]
data = subprocess.run(command , stdout=subprocess.PIPE)
duration = data.stdout.decode('utf-8')
# Get frame roughly 35% through video
grabTime = str( int( float( duration.split(".")[0] ) * 0.35) )
command = ["ffmpeg", "-ss", grabTime, "-an", "-i", full_path, "-s", "320x180", "-vframes", "1", hash_img_pth]
proc = subprocess.Popen(command, stdout=subprocess.PIPE)
proc.wait()
except Exception as e:
print("Video thumbnail generation issue in thread:")
print( repr(e) )
self.logger.debug(repr(e))
def create_scaled_image(self, path, wxh):
try:
pixbuf = Gtk.Image.new_from_file(path).get_pixbuf()
scaled_pixbuf = pixbuf.scale_simple(wxh[0], wxh[1], 2) # 2 = BILINEAR and is best by default
return Gtk.Image.new_from_pixbuf(scaled_pixbuf)
except Exception as e:
print("Image Scaling Issue:")
print( repr(e) )
return None
def create_from_file(self, path):
try:
return Gtk.Image.new_from_file(path)
except Exception as e:
print("Image from file Issue:")
print( repr(e) )
return None
def return_generic_icon(self):
return Gtk.Image.new_from_file(self.DEFAULT_ICON)