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): def _exec_grep_query(self, widget=None, eve=None):
query = widget.get_text() query = widget.get_text()
if not query.strip() in ("", None): if not query.strip() in ("", None):
self.grep_query = query self.grep_query = query

View File

@ -1,16 +1,11 @@
# Python imports # Python imports
import os
# Lib imports # Lib imports
import gi import gi
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
gi.require_version('Gdk', '3.0')
from gi.repository import Gtk from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GLib from gi.repository import GLib
from gi.repository import Gio
from gi.repository import GdkPixbuf
# Application imports # Application imports
from ...widgets.tab_header_widget import TabHeaderWidget from ...widgets.tab_header_widget import TabHeaderWidget
@ -45,32 +40,12 @@ class GridMixin:
def create_icon(self, i, tab, store, dir, file): def create_icon(self, i, tab, store, dir, file):
icon = tab.create_icon(dir, file) icon = tab.create_icon(dir, file)
GLib.idle_add(self.update_store, *(i, store, icon, tab, dir, file,)) GLib.idle_add(self.update_store, *(i, store, icon,))
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)
def update_store(self, i, store, icon):
itr = store.get_iter(i) itr = store.get_iter(i)
store.set_value(itr, 0, icon) 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): def create_tab_widget(self, tab):
return TabHeaderWidget(tab, self.close_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 # Python imports
import threading, json import threading
import json
from os import path from os import path
# Lib imports # Lib imports
@ -8,10 +9,6 @@ from os import path
from .window import Window 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: 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 # Python imports
import os, subprocess, threading, hashlib import hashlib
from os.path import isfile from os.path import isfile
# Lib imports # Lib imports
import gi import gi
gi.require_version('GdkPixbuf', '2.0') 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: try:
from PIL import Image as PImage from PIL import Image as PImage
except Exception as e: except Exception as e:
PImage = None PImage = None
# Application imports # Application imports
from .mixins.desktopiconmixin import DesktopIconMixin
from .mixins.videoiconmixin import VideoIconMixin 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): def create_icon(self, dir, file):
full_path = f"{dir}/{file}" full_path = f"{dir}/{file}"
return self.get_icon_image(dir, file, full_path) return self.get_icon_image(dir, file, full_path)
@ -34,63 +31,81 @@ class Icon(DesktopIconMixin, VideoIconMixin):
try: try:
thumbnl = None thumbnl = None
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) thumbnl = self.create_thumbnail(dir, file, 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
thumbnl = self.create_blender_thumbnail(dir, file, 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.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 return thumbnl
except Exception: except Exception:
... ...
return None return self.return_generic_icon()
def create_thumbnail(self, dir, file, scrub_percent = "65%"):
full_path = f"{dir}/{file}"
def create_blender_thumbnail(self, dir, file, full_path=None):
try: try:
file_hash = hashlib.sha256(str.encode(full_path)).hexdigest() file_hash = hashlib.sha256(str.encode(full_path)).hexdigest()
hash_img_pth = f"{self.ABS_THUMBS_PTH}/{file_hash}.jpg" hash_img_path = f"{self.ABS_THUMBS_PTH}/{file_hash}.png"
if isfile(hash_img_pth) == False: if not isfile(hash_img_path):
self.generate_video_thumbnail(full_path, hash_img_pth, scrub_percent) self.generate_blender_thumbnail(full_path, hash_img_path)
thumbnl = self.create_scaled_image(hash_img_pth, self.video_icon_wh) return self.create_scaled_image(hash_img_path, 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
except Exception as e: except Exception as e:
print("Thumbnail generation issue:") print("Blender thumbnail generation issue:")
print( repr(e) ) 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: if not wxh:
wxh = self.video_icon_wh wxh = self.video_icon_wh
if path: if full_path:
try: try:
if path.lower().endswith(".gif"): if full_path.lower().endswith(".gif"):
return GdkPixbuf.PixbufAnimation.new_from_file(path) \ return GdkPixbuf.PixbufAnimation.new_from_file(full_path) \
.get_static_image() \ .get_static_image() \
.scale_simple(wxh[0], wxh[1], GdkPixbuf.InterpType.BILINEAR) .scale_simple(wxh[0], wxh[1], GdkPixbuf.InterpType.BILINEAR)
elif path.lower().endswith(".webp") and PImage: elif full_path.lower().endswith(".webp") and PImage:
return self.image2pixbuf(path, wxh) 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: except Exception as e:
print("Image Scaling Issue:") print("Image Scaling Issue:")
print( repr(e) ) print( repr(e) )
return None return None
def image2pixbuf(self, path, wxh): def image2pixbuf(self, full_path, wxh):
"""Convert Pillow image to GdkPixbuf""" """Convert Pillow image to GdkPixbuf"""
im = PImage.open(path) im = PImage.open(full_path)
data = im.tobytes() data = im.tobytes()
data = GLib.Bytes.new(data) data = GLib.Bytes.new(data)
w, h = im.size w, h = im.size
@ -100,14 +115,27 @@ class Icon(DesktopIconMixin, VideoIconMixin):
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, path): def create_from_file(self, full_path):
try: try:
return GdkPixbuf.Pixbuf.new_from_file(path) return GdkPixbuf.Pixbuf.new_from_file(full_path)
except Exception as e: except Exception as e:
print("Image from file Issue:") print("Image from file Issue:")
print( repr(e) ) print( repr(e) )
return None 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): def return_generic_icon(self):
return GdkPixbuf.Pixbuf.new_from_file(self.DEFAULT_ICON) 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 # Python imports
import os, subprocess, hashlib import os
from os.path import isfile from os.path import isfile
import subprocess
import hashlib
# Gtk 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
@ -13,6 +14,8 @@ from gi.repository import Gio
from .xdg.DesktopEntry import DesktopEntry from .xdg.DesktopEntry import DesktopEntry
class DesktopIconMixin: class DesktopIconMixin:
def parse_desktop_files(self, full_path): def parse_desktop_files(self, full_path):
try: 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 import subprocess
# Gtk imports # Lib imports
# Application imports # Application imports
class VideoIconMixin: 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: 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() proc.wait()
except Exception as e: except Exception as e:
self.logger.debug(repr(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 proc = None
try: try:
# Stream duration # Stream duration
@ -44,7 +46,7 @@ class VideoIconMixin:
# Get frame roughly 35% through video # Get frame roughly 35% through video
grabTime = str( int( float( duration.split(".")[0] ) * 0.35) ) 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 = subprocess.Popen(command, stdout=subprocess.PIPE)
proc.wait() proc.wait()
except Exception as e: except Exception as e:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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