Import cleanup and thubnailer logic changes

This commit is contained in:
itdominator 2023-01-29 00:10:09 -06:00
parent 9e7dbb4245
commit f440ff3d96
20 changed files with 136 additions and 107 deletions

View File

@ -65,6 +65,7 @@ class GrepSearchMixin:
def _exec_grep_query(self, widget=None, eve=None):
query = widget.get_text()
if not query.strip() in ("", None):
self.grep_query = query

View File

@ -1,16 +1,11 @@
# Python imports
import os
# Lib imports
import gi
gi.require_version("Gtk", "3.0")
gi.require_version('Gdk', '3.0')
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GLib
from gi.repository import Gio
from gi.repository import GdkPixbuf
# Application imports
from ...widgets.tab_header_widget import TabHeaderWidget
@ -45,32 +40,12 @@ class GridMixin:
def create_icon(self, i, tab, store, dir, file):
icon = tab.create_icon(dir, file)
GLib.idle_add(self.update_store, *(i, store, icon, tab, dir, file,))
def update_store(self, i, store, icon, tab, dir, file):
if not icon:
path = f"{dir}/{file}"
icon = self.get_system_thumbnail(path, tab.sys_icon_wh[0])
if not icon:
icon = GdkPixbuf.Pixbuf.new_from_file(tab.DEFAULT_ICON)
GLib.idle_add(self.update_store, *(i, store, icon,))
def update_store(self, i, store, icon):
itr = store.get_iter(i)
store.set_value(itr, 0, icon)
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 create_tab_widget(self, tab):
return TabHeaderWidget(tab, self.close_tab)

View File

@ -1,3 +1,3 @@
"""
Root of ShellFM
Root of ShellFM
"""

View File

@ -1,3 +1,3 @@
"""
Window module
Window module
"""

View File

@ -1,5 +1,6 @@
# Python imports
import threading, json
import threading
import json
from os import path
# Lib imports
@ -8,10 +9,6 @@ from os import path
from .window import Window
def threaded(fn):
def wrapper(*args, **kwargs):
threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start()
return wrapper
class WindowController:

View File

@ -1,3 +1,3 @@
"""
Tabs module
Tabs module
"""

View File

@ -1,3 +1,3 @@
"""
Icons module
Icons module
"""

View File

@ -1,31 +1,28 @@
# Python Imports
import os, subprocess, threading, hashlib
# Python imports
import hashlib
from os.path import isfile
# Lib imports
import gi
gi.require_version('GdkPixbuf', '2.0')
from gi.repository import GdkPixbuf, GLib
from gi.repository import GLib
from gi.repository import Gio
from gi.repository import GdkPixbuf
try:
from PIL import Image as PImage
except Exception as e:
PImage = None
# Application imports
from .mixins.desktopiconmixin import DesktopIconMixin
from .mixins.videoiconmixin import VideoIconMixin
from .mixins.meshsiconmixin import MeshsIconMixin
from .mixins.desktopiconmixin import DesktopIconMixin
def threaded(fn):
def wrapper(*args, **kwargs):
threading.Thread(target=fn, args=args, kwargs=kwargs).start()
return wrapper
class Icon(DesktopIconMixin, VideoIconMixin):
class Icon(DesktopIconMixin, VideoIconMixin, MeshsIconMixin):
def create_icon(self, dir, file):
full_path = f"{dir}/{file}"
return self.get_icon_image(dir, file, full_path)
@ -34,63 +31,81 @@ class Icon(DesktopIconMixin, VideoIconMixin):
try:
thumbnl = None
if file.lower().endswith(self.fmeshs): # 3D Mesh icon
...
if file.lower().endswith(self.fvideos): # Video icon
thumbnl = self.create_thumbnail(dir, file)
thumbnl = self.create_thumbnail(dir, file, full_path)
elif file.lower().endswith(self.fimages): # Image Icon
thumbnl = self.create_scaled_image(full_path)
elif file.lower().endswith( (".blend",) ): # Blender icon
thumbnl = self.create_blender_thumbnail(dir, file, full_path)
elif full_path.lower().endswith( ('.desktop',) ): # .desktop file parsing
thumbnl = self.parse_desktop_files(full_path)
if not thumbnl:
thumbnl = self.get_system_thumbnail(full_path, self.sys_icon_wh[0])
if not thumbnl:
thumbnl = self.return_generic_icon()
return thumbnl
except Exception:
...
return None
def create_thumbnail(self, dir, file, scrub_percent = "65%"):
full_path = f"{dir}/{file}"
return self.return_generic_icon()
def create_blender_thumbnail(self, dir, file, full_path=None):
try:
file_hash = hashlib.sha256(str.encode(full_path)).hexdigest()
hash_img_pth = f"{self.ABS_THUMBS_PTH}/{file_hash}.jpg"
if isfile(hash_img_pth) == False:
self.generate_video_thumbnail(full_path, hash_img_pth, scrub_percent)
file_hash = hashlib.sha256(str.encode(full_path)).hexdigest()
hash_img_path = f"{self.ABS_THUMBS_PTH}/{file_hash}.png"
if not isfile(hash_img_path):
self.generate_blender_thumbnail(full_path, hash_img_path)
thumbnl = self.create_scaled_image(hash_img_pth, self.video_icon_wh)
if thumbnl == None: # If no icon whatsoever, return internal default
thumbnl = GdkPixbuf.Pixbuf.new_from_file(f"{self.DEFAULT_ICONS}/video.png")
return thumbnl
return self.create_scaled_image(hash_img_path, self.video_icon_wh)
except Exception as e:
print("Thumbnail generation issue:")
print("Blender thumbnail generation issue:")
print( repr(e) )
return GdkPixbuf.Pixbuf.new_from_file(f"{self.DEFAULT_ICONS}/video.png")
return None
def create_thumbnail(self, dir, file, full_path=None, scrub_percent = "65%"):
try:
file_hash = hashlib.sha256(str.encode(full_path)).hexdigest()
hash_img_path = f"{self.ABS_THUMBS_PTH}/{file_hash}.jpg"
if not isfile(hash_img_path):
self.generate_video_thumbnail(full_path, hash_img_path, scrub_percent)
return self.create_scaled_image(hash_img_path, self.video_icon_wh)
except Exception as e:
print("Image/Video thumbnail generation issue:")
print( repr(e) )
return None
def create_scaled_image(self, path, wxh = None):
def create_scaled_image(self, full_path, wxh = None):
if not wxh:
wxh = self.video_icon_wh
if path:
if full_path:
try:
if path.lower().endswith(".gif"):
return GdkPixbuf.PixbufAnimation.new_from_file(path) \
if full_path.lower().endswith(".gif"):
return GdkPixbuf.PixbufAnimation.new_from_file(full_path) \
.get_static_image() \
.scale_simple(wxh[0], wxh[1], GdkPixbuf.InterpType.BILINEAR)
elif path.lower().endswith(".webp") and PImage:
return self.image2pixbuf(path, wxh)
elif full_path.lower().endswith(".webp") and PImage:
return self.image2pixbuf(full_path, wxh)
return GdkPixbuf.Pixbuf.new_from_file_at_scale(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:
print("Image Scaling Issue:")
print( repr(e) )
return None
def image2pixbuf(self, path, wxh):
def image2pixbuf(self, full_path, wxh):
"""Convert Pillow image to GdkPixbuf"""
im = PImage.open(path)
im = PImage.open(full_path)
data = im.tobytes()
data = GLib.Bytes.new(data)
w, h = im.size
@ -100,14 +115,27 @@ class Icon(DesktopIconMixin, VideoIconMixin):
return pixbuf.scale_simple(wxh[0], wxh[1], 2) # BILINEAR = 2
def create_from_file(self, path):
def create_from_file(self, full_path):
try:
return GdkPixbuf.Pixbuf.new_from_file(path)
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

@ -1,3 +1,3 @@
"""
Icons mixins module
Icons mixins module
"""

View File

@ -1,11 +1,12 @@
# Python Imports
import os, subprocess, hashlib
# Python imports
import os
from os.path import isfile
import subprocess
import hashlib
# Gtk imports
# Lib imports
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import Gio
@ -13,6 +14,8 @@ from gi.repository import Gio
from .xdg.DesktopEntry import DesktopEntry
class DesktopIconMixin:
def parse_desktop_files(self, full_path):
try:

View File

@ -0,0 +1,17 @@
# Python imports
import subprocess
# Lib imports
# Application imports
class MeshsIconMixin:
def generate_blender_thumbnail(self, full_path, hash_img_path):
try:
proc = subprocess.Popen([self.BLENDER_THUMBNLR, full_path, hash_img_path])
proc.wait()
except Exception as e:
self.logger.debug(repr(e))

View File

@ -1,22 +1,24 @@
# Python Imports
# Python imports
import subprocess
# Gtk imports
# Lib imports
# Application imports
class VideoIconMixin:
def generate_video_thumbnail(self, full_path, hash_img_pth, scrub_percent = "65%"):
def generate_video_thumbnail(self, full_path, hash_img_path, scrub_percent = "65%"):
try:
proc = subprocess.Popen([self.FFMPG_THUMBNLR, "-t", scrub_percent, "-s", "300", "-c", "jpg", "-i", full_path, "-o", hash_img_pth])
proc = subprocess.Popen([self.FFMPG_THUMBNLR, "-t", scrub_percent, "-s", "300", "-c", "jpg", "-i", full_path, "-o", hash_img_path])
proc.wait()
except Exception as e:
self.logger.debug(repr(e))
self.ffprobe_generate_video_thumbnail(full_path, hash_img_pth)
self.ffprobe_generate_video_thumbnail(full_path, hash_img_path)
def ffprobe_generate_video_thumbnail(self, full_path, hash_img_pth):
def ffprobe_generate_video_thumbnail(self, full_path, hash_img_path):
proc = None
try:
# Stream duration
@ -44,7 +46,7 @@ class VideoIconMixin:
# 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]
command = ["ffmpeg", "-ss", grabTime, "-an", "-i", full_path, "-s", "320x180", "-vframes", "1", hash_img_path]
proc = subprocess.Popen(command, stdout=subprocess.PIPE)
proc.wait()
except Exception as e:

View File

@ -6,6 +6,8 @@ import os
# Application imports
class Path:
def get_home(self) -> str:
return os.path.expanduser("~") + self.subpath

View File

@ -1,23 +1,24 @@
# Python imports
import hashlib, re
import hashlib
import re
from os import listdir
from os.path import isdir, isfile, join
from os.path import isdir
from os.path import isfile
from os.path import join
from random import randint
# Lib imports
# Application imports
from .utils.settings import Settings
from .utils.launcher import Launcher
from .utils.filehandler import FileHandler
from .icons.icon import Icon
from .path import Path
class Tab(Settings, FileHandler, Launcher, Icon, Path):
def __init__(self):
self.logger = None

View File

@ -1,3 +1,3 @@
"""
Utils module
Utils module
"""

View File

@ -1,5 +1,6 @@
# Python imports
import os, shutil
import os
import shutil
# Lib imports

View File

@ -1,16 +1,12 @@
# System import
import os, threading, subprocess
# Python imports
import os
import subprocess
# Lib imports
# Apoplication imports
def threaded(fn):
def wrapper(*args, **kwargs):
threading.Thread(target=fn, args=args, kwargs=kwargs).start()
return wrapper
class Launcher:

View File

@ -1,4 +1,4 @@
# System import
# Python imports
import json
import os
from os import path
@ -10,6 +10,7 @@ from os import path
class Settings:
logger = None
@ -22,8 +23,9 @@ class Settings:
GTK_ORIENTATION = 1 # HORIZONTAL (0) VERTICAL (1)
DEFAULT_ICONS = f"{CONFIG_PATH}/icons"
DEFAULT_ICON = f"{DEFAULT_ICONS}/text.png"
FFMPG_THUMBNLR = f"{CONFIG_PATH}/ffmpegthumbnailer" # Thumbnail generator binary
REMUX_FOLDER = f"{USER_HOME}/.remuxs" # Remuxed files folder
FFMPG_THUMBNLR = f"{CONFIG_PATH}/ffmpegthumbnailer" # Thumbnail generator binary
BLENDER_THUMBNLR = f"{CONFIG_PATH}/blender-thumbnailer" # Blender thumbnail generator binary
REMUX_FOLDER = f"{USER_HOME}/.remuxs" # Remuxed files folder
ICON_DIRS = ["/usr/share/icons", f"{USER_HOME}/.icons" "/usr/share/pixmaps"]
BASE_THUMBS_PTH = f"{USER_HOME}/.thumbnails" # Used for thumbnail generation
@ -53,7 +55,8 @@ class Settings:
subpath = config["base_of_home"]
STEAM_CDN_URL = config["steam_cdn_url"]
FFMPG_THUMBNLR = FFMPG_THUMBNLR if config["thumbnailer_path"] == "" else config["thumbnailer_path"]
FFMPG_THUMBNLR = FFMPG_THUMBNLR if config["thumbnailer_path"] == "" else config["thumbnailer_path"]
BLENDER_THUMBNLR = BLENDER_THUMBNLR if config["blender_thumbnailer_path"] == "" else config["blender_thumbnailer_path"]
HIDE_HIDDEN_FILES = True if config["hide_hidden_files"] == "true" else False
go_past_home = True if config["go_past_home"] == "" else config["go_past_home"]
lock_folder = True if config["lock_folder"] == "true" else False
@ -75,6 +78,7 @@ class Settings:
# Filters
filters = settings["filters"]
fmeshs = tuple(filters["meshs"])
fcode = tuple(filters["code"])
fvideos = tuple(filters["videos"])
foffice = tuple(filters["office"])

View File

@ -1,14 +1,14 @@
# Python imports
from random import randint
# Lib imports
# Application imports
from .tabs.tab import Tab
class Window:
def __init__(self):
self._id_length: int = 10

View File

@ -3,6 +3,7 @@
"base_of_home": "",
"hide_hidden_files": "true",
"thumbnailer_path": "ffmpegthumbnailer",
"blender_thumbnailer_path": "",
"go_past_home": "true",
"lock_folder": "false",
"locked_folders": "venv::::flasks",
@ -23,6 +24,7 @@
"remux_folder_max_disk_usage": "8589934592"
},
"filters": {
"meshs": [".dae", ".fbx", ".gltf", ".obj", ".stl"],
"code": [".cpp", ".css", ".c", ".go", ".html", ".htm", ".java", ".js", ".json", ".lua", ".md", ".py", ".rs", ".toml", ".xml", ".pom"],
"videos": [".mkv", ".mp4", ".webm", ".avi", ".mov", ".m4v", ".mpg", ".mpeg", ".wmv", ".flv"],
"office": [".doc", ".docx", ".xls", ".xlsx", ".xlt", ".xltx", ".xlm", ".ppt", ".pptx", ".pps", ".ppsx", ".odt", ".rtf"],