From ce00970171fee2904ec948c17f7e06d48b3d39df Mon Sep 17 00:00:00 2001 From: itdominator <1itdominator@gmail.com> Date: Sat, 29 Jun 2024 21:37:44 -0500 Subject: [PATCH] moved thumbnail generation to plugin; extended plugin loading for pre and post window loading --- plugins/README.md | 1 + plugins/template/manifest.json | 3 +- plugins/thumbnailer/__init__.py | 3 + plugins/thumbnailer/__main__.py | 3 + .../thumbnailer}/icons/__init__.py | 0 plugins/thumbnailer/icons/controller.py | 73 +++++++++++++ .../thumbnailer}/icons/icon.py | 0 .../thumbnailer}/icons/mixins/__init__.py | 0 .../icons/mixins/desktopiconmixin.py | 0 .../icons/mixins/meshsiconmixin.py | 2 +- .../icons/mixins/videoiconmixin.py | 5 +- .../icons/mixins/xdg/BaseDirectory.py | 0 .../thumbnailer}/icons/mixins/xdg/Config.py | 0 .../icons/mixins/xdg/DesktopEntry.py | 0 .../icons/mixins/xdg/Exceptions.py | 0 .../icons/mixins/xdg/IconTheme.py | 0 .../thumbnailer}/icons/mixins/xdg/IniFile.py | 0 .../thumbnailer}/icons/mixins/xdg/Locale.py | 0 .../thumbnailer}/icons/mixins/xdg/Menu.py | 0 .../icons/mixins/xdg/MenuEditor.py | 0 .../thumbnailer}/icons/mixins/xdg/Mime.py | 0 .../icons/mixins/xdg/RecentFiles.py | 0 .../thumbnailer}/icons/mixins/xdg/__init__.py | 0 .../thumbnailer}/icons/mixins/xdg/util.py | 0 plugins/thumbnailer/manifest.json | 12 +++ plugins/thumbnailer/plugin.py | 59 ++++++++++ plugins/thumbnailer/settings.json | 101 ++++++++++++++++++ plugins/trasher/plugin.py | 2 + plugins/trasher/trash.py | 2 +- plugins/trasher/xdgtrash.py | 2 +- src/solarfm/core/controller.py | 9 +- src/solarfm/core/controller_data.py | 4 +- src/solarfm/core/mixins/ui/grid_mixin.py | 3 +- src/solarfm/core/ui_mixin.py | 2 +- src/solarfm/plugins/manifest.py | 32 +++--- src/solarfm/plugins/plugins_controller.py | 61 ++++++++--- src/solarfm/shellfm/windows/tabs/tab.py | 37 ++----- .../shellfm/windows/tabs/utils/launcher.py | 20 ++-- .../shellfm/windows/tabs/utils/settings.py | 37 ------- user_config/usr/share/solarfm/settings.json | 11 +- 40 files changed, 359 insertions(+), 125 deletions(-) create mode 100644 plugins/thumbnailer/__init__.py create mode 100644 plugins/thumbnailer/__main__.py rename {src/solarfm/shellfm/windows/tabs => plugins/thumbnailer}/icons/__init__.py (100%) create mode 100644 plugins/thumbnailer/icons/controller.py rename {src/solarfm/shellfm/windows/tabs => plugins/thumbnailer}/icons/icon.py (100%) rename {src/solarfm/shellfm/windows/tabs => plugins/thumbnailer}/icons/mixins/__init__.py (100%) rename {src/solarfm/shellfm/windows/tabs => plugins/thumbnailer}/icons/mixins/desktopiconmixin.py (100%) rename {src/solarfm/shellfm/windows/tabs => plugins/thumbnailer}/icons/mixins/meshsiconmixin.py (89%) rename {src/solarfm/shellfm/windows/tabs => plugins/thumbnailer}/icons/mixins/videoiconmixin.py (95%) rename {src/solarfm/shellfm/windows/tabs => plugins/thumbnailer}/icons/mixins/xdg/BaseDirectory.py (100%) rename {src/solarfm/shellfm/windows/tabs => plugins/thumbnailer}/icons/mixins/xdg/Config.py (100%) rename {src/solarfm/shellfm/windows/tabs => plugins/thumbnailer}/icons/mixins/xdg/DesktopEntry.py (100%) rename {src/solarfm/shellfm/windows/tabs => plugins/thumbnailer}/icons/mixins/xdg/Exceptions.py (100%) rename {src/solarfm/shellfm/windows/tabs => plugins/thumbnailer}/icons/mixins/xdg/IconTheme.py (100%) rename {src/solarfm/shellfm/windows/tabs => plugins/thumbnailer}/icons/mixins/xdg/IniFile.py (100%) rename {src/solarfm/shellfm/windows/tabs => plugins/thumbnailer}/icons/mixins/xdg/Locale.py (100%) rename {src/solarfm/shellfm/windows/tabs => plugins/thumbnailer}/icons/mixins/xdg/Menu.py (100%) rename {src/solarfm/shellfm/windows/tabs => plugins/thumbnailer}/icons/mixins/xdg/MenuEditor.py (100%) rename {src/solarfm/shellfm/windows/tabs => plugins/thumbnailer}/icons/mixins/xdg/Mime.py (100%) rename {src/solarfm/shellfm/windows/tabs => plugins/thumbnailer}/icons/mixins/xdg/RecentFiles.py (100%) rename {src/solarfm/shellfm/windows/tabs => plugins/thumbnailer}/icons/mixins/xdg/__init__.py (100%) rename {src/solarfm/shellfm/windows/tabs => plugins/thumbnailer}/icons/mixins/xdg/util.py (100%) create mode 100644 plugins/thumbnailer/manifest.json create mode 100644 plugins/thumbnailer/plugin.py create mode 100644 plugins/thumbnailer/settings.json diff --git a/plugins/README.md b/plugins/README.md index 1f18e50..f475c08 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -14,6 +14,7 @@ class Manifest: 'ui_target': "plugin_control_list", 'pass_fm_events': "true" } + pre_launch: bool = False ``` diff --git a/plugins/template/manifest.json b/plugins/template/manifest.json index 4dcbf47..10483da 100644 --- a/plugins/template/manifest.json +++ b/plugins/template/manifest.json @@ -8,6 +8,7 @@ "ui_target": "plugin_control_list", "pass_fm_events": "true", "bind_keys": ["Example Plugin||send_message:f"] - } + }, + "pre_launch": "false" } } diff --git a/plugins/thumbnailer/__init__.py b/plugins/thumbnailer/__init__.py new file mode 100644 index 0000000..d36fa8c --- /dev/null +++ b/plugins/thumbnailer/__init__.py @@ -0,0 +1,3 @@ +""" + Pligin Module +""" diff --git a/plugins/thumbnailer/__main__.py b/plugins/thumbnailer/__main__.py new file mode 100644 index 0000000..a576329 --- /dev/null +++ b/plugins/thumbnailer/__main__.py @@ -0,0 +1,3 @@ +""" + Pligin Package +""" diff --git a/src/solarfm/shellfm/windows/tabs/icons/__init__.py b/plugins/thumbnailer/icons/__init__.py similarity index 100% rename from src/solarfm/shellfm/windows/tabs/icons/__init__.py rename to plugins/thumbnailer/icons/__init__.py diff --git a/plugins/thumbnailer/icons/controller.py b/plugins/thumbnailer/icons/controller.py new file mode 100644 index 0000000..732cdb1 --- /dev/null +++ b/plugins/thumbnailer/icons/controller.py @@ -0,0 +1,73 @@ +# Python imports +import json +import os +from os import path + +# Lib imports +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk + +# Application imports +from .icon import Icon + + + +class IconController(Icon): + def __init__(self): + CURRENT_PATH = os.path.dirname(os.path.realpath(__file__)) + + # NOTE: app_name should be defined using python 'builtins' and so too must be logger used in the various classes + app_name_exists = False + try: + app_name + app_name_exists = True + except Exception as e: + ... + + APP_CONTEXT = f"{app_name.lower()}" if app_name_exists else "shellfm" + USR_APP_CONTEXT = f"/usr/share/{APP_CONTEXT}" + USER_HOME = path.expanduser('~') + CONFIG_PATH = f"{USER_HOME}/.config/{APP_CONTEXT}" + self.DEFAULT_ICONS = f"{CONFIG_PATH}/icons" + self.DEFAULT_ICON = f"{self.DEFAULT_ICONS}/text.png" + self.FFMPG_THUMBNLR = f"{CONFIG_PATH}/ffmpegthumbnailer" # Thumbnail generator binary + self.BLENDER_THUMBNLR = f"{CONFIG_PATH}/blender-thumbnailer" # Blender thumbnail generator binary + + self.ICON_DIRS = ["/usr/share/icons", f"{USER_HOME}/.icons" "/usr/share/pixmaps"] + self.BASE_THUMBS_PTH = f"{USER_HOME}/.thumbnails" + self.ABS_THUMBS_PTH = f"{self.BASE_THUMBS_PTH}/normal" + self.STEAM_ICONS_PTH = f"{self.BASE_THUMBS_PTH}/steam_icons" + + if not path.isdir(self.BASE_THUMBS_PTH): + os.mkdir(self.BASE_THUMBS_PTH) + + if not path.isdir(self.ABS_THUMBS_PTH): + os.mkdir(self.ABS_THUMBS_PTH) + + if not path.isdir(self.STEAM_ICONS_PTH): + os.mkdir(self.STEAM_ICONS_PTH) + + if not os.path.exists(self.DEFAULT_ICONS): + self.DEFAULT_ICONS = f"{USR_APP_CONTEXT}/icons" + self.DEFAULT_ICON = f"{self.DEFAULT_ICONS}/text.png" + + CONFIG_FILE = f"{CURRENT_PATH}/../settings.json" + with open(CONFIG_FILE) as f: + settings = json.load(f) + config = settings["config"] + + self.container_icon_wh = config["container_icon_wh"] + self.video_icon_wh = config["video_icon_wh"] + self.sys_icon_wh = config["sys_icon_wh"] + + # Filters + filters = settings["filters"] + self.fmeshs = tuple(filters["meshs"]) + self.fcode = tuple(filters["code"]) + self.fvideos = tuple(filters["videos"]) + self.foffice = tuple(filters["office"]) + self.fimages = tuple(filters["images"]) + self.ftext = tuple(filters["text"]) + self.fmusic = tuple(filters["music"]) + self.fpdf = tuple(filters["pdf"]) diff --git a/src/solarfm/shellfm/windows/tabs/icons/icon.py b/plugins/thumbnailer/icons/icon.py similarity index 100% rename from src/solarfm/shellfm/windows/tabs/icons/icon.py rename to plugins/thumbnailer/icons/icon.py diff --git a/src/solarfm/shellfm/windows/tabs/icons/mixins/__init__.py b/plugins/thumbnailer/icons/mixins/__init__.py similarity index 100% rename from src/solarfm/shellfm/windows/tabs/icons/mixins/__init__.py rename to plugins/thumbnailer/icons/mixins/__init__.py diff --git a/src/solarfm/shellfm/windows/tabs/icons/mixins/desktopiconmixin.py b/plugins/thumbnailer/icons/mixins/desktopiconmixin.py similarity index 100% rename from src/solarfm/shellfm/windows/tabs/icons/mixins/desktopiconmixin.py rename to plugins/thumbnailer/icons/mixins/desktopiconmixin.py diff --git a/src/solarfm/shellfm/windows/tabs/icons/mixins/meshsiconmixin.py b/plugins/thumbnailer/icons/mixins/meshsiconmixin.py similarity index 89% rename from src/solarfm/shellfm/windows/tabs/icons/mixins/meshsiconmixin.py rename to plugins/thumbnailer/icons/mixins/meshsiconmixin.py index 0d62636..8f7b057 100644 --- a/src/solarfm/shellfm/windows/tabs/icons/mixins/meshsiconmixin.py +++ b/plugins/thumbnailer/icons/mixins/meshsiconmixin.py @@ -14,4 +14,4 @@ class MeshsIconMixin: proc = subprocess.Popen([self.BLENDER_THUMBNLR, full_path, hash_img_path]) proc.wait() except Exception as e: - self.logger.debug(repr(e)) + logger.debug(repr(e)) diff --git a/src/solarfm/shellfm/windows/tabs/icons/mixins/videoiconmixin.py b/plugins/thumbnailer/icons/mixins/videoiconmixin.py similarity index 95% rename from src/solarfm/shellfm/windows/tabs/icons/mixins/videoiconmixin.py rename to plugins/thumbnailer/icons/mixins/videoiconmixin.py index 60fd16b..324cbb4 100644 --- a/src/solarfm/shellfm/windows/tabs/icons/mixins/videoiconmixin.py +++ b/plugins/thumbnailer/icons/mixins/videoiconmixin.py @@ -14,7 +14,7 @@ class VideoIconMixin: 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)) + logger.info(repr(e)) self.ffprobe_generate_video_thumbnail(full_path, hash_img_path) @@ -51,5 +51,4 @@ class VideoIconMixin: proc.wait() except Exception as e: print("Video thumbnail generation issue in thread:") - print( repr(e) ) - self.logger.debug(repr(e)) + logger.info(repr(e)) diff --git a/src/solarfm/shellfm/windows/tabs/icons/mixins/xdg/BaseDirectory.py b/plugins/thumbnailer/icons/mixins/xdg/BaseDirectory.py similarity index 100% rename from src/solarfm/shellfm/windows/tabs/icons/mixins/xdg/BaseDirectory.py rename to plugins/thumbnailer/icons/mixins/xdg/BaseDirectory.py diff --git a/src/solarfm/shellfm/windows/tabs/icons/mixins/xdg/Config.py b/plugins/thumbnailer/icons/mixins/xdg/Config.py similarity index 100% rename from src/solarfm/shellfm/windows/tabs/icons/mixins/xdg/Config.py rename to plugins/thumbnailer/icons/mixins/xdg/Config.py diff --git a/src/solarfm/shellfm/windows/tabs/icons/mixins/xdg/DesktopEntry.py b/plugins/thumbnailer/icons/mixins/xdg/DesktopEntry.py similarity index 100% rename from src/solarfm/shellfm/windows/tabs/icons/mixins/xdg/DesktopEntry.py rename to plugins/thumbnailer/icons/mixins/xdg/DesktopEntry.py diff --git a/src/solarfm/shellfm/windows/tabs/icons/mixins/xdg/Exceptions.py b/plugins/thumbnailer/icons/mixins/xdg/Exceptions.py similarity index 100% rename from src/solarfm/shellfm/windows/tabs/icons/mixins/xdg/Exceptions.py rename to plugins/thumbnailer/icons/mixins/xdg/Exceptions.py diff --git a/src/solarfm/shellfm/windows/tabs/icons/mixins/xdg/IconTheme.py b/plugins/thumbnailer/icons/mixins/xdg/IconTheme.py similarity index 100% rename from src/solarfm/shellfm/windows/tabs/icons/mixins/xdg/IconTheme.py rename to plugins/thumbnailer/icons/mixins/xdg/IconTheme.py diff --git a/src/solarfm/shellfm/windows/tabs/icons/mixins/xdg/IniFile.py b/plugins/thumbnailer/icons/mixins/xdg/IniFile.py similarity index 100% rename from src/solarfm/shellfm/windows/tabs/icons/mixins/xdg/IniFile.py rename to plugins/thumbnailer/icons/mixins/xdg/IniFile.py diff --git a/src/solarfm/shellfm/windows/tabs/icons/mixins/xdg/Locale.py b/plugins/thumbnailer/icons/mixins/xdg/Locale.py similarity index 100% rename from src/solarfm/shellfm/windows/tabs/icons/mixins/xdg/Locale.py rename to plugins/thumbnailer/icons/mixins/xdg/Locale.py diff --git a/src/solarfm/shellfm/windows/tabs/icons/mixins/xdg/Menu.py b/plugins/thumbnailer/icons/mixins/xdg/Menu.py similarity index 100% rename from src/solarfm/shellfm/windows/tabs/icons/mixins/xdg/Menu.py rename to plugins/thumbnailer/icons/mixins/xdg/Menu.py diff --git a/src/solarfm/shellfm/windows/tabs/icons/mixins/xdg/MenuEditor.py b/plugins/thumbnailer/icons/mixins/xdg/MenuEditor.py similarity index 100% rename from src/solarfm/shellfm/windows/tabs/icons/mixins/xdg/MenuEditor.py rename to plugins/thumbnailer/icons/mixins/xdg/MenuEditor.py diff --git a/src/solarfm/shellfm/windows/tabs/icons/mixins/xdg/Mime.py b/plugins/thumbnailer/icons/mixins/xdg/Mime.py similarity index 100% rename from src/solarfm/shellfm/windows/tabs/icons/mixins/xdg/Mime.py rename to plugins/thumbnailer/icons/mixins/xdg/Mime.py diff --git a/src/solarfm/shellfm/windows/tabs/icons/mixins/xdg/RecentFiles.py b/plugins/thumbnailer/icons/mixins/xdg/RecentFiles.py similarity index 100% rename from src/solarfm/shellfm/windows/tabs/icons/mixins/xdg/RecentFiles.py rename to plugins/thumbnailer/icons/mixins/xdg/RecentFiles.py diff --git a/src/solarfm/shellfm/windows/tabs/icons/mixins/xdg/__init__.py b/plugins/thumbnailer/icons/mixins/xdg/__init__.py similarity index 100% rename from src/solarfm/shellfm/windows/tabs/icons/mixins/xdg/__init__.py rename to plugins/thumbnailer/icons/mixins/xdg/__init__.py diff --git a/src/solarfm/shellfm/windows/tabs/icons/mixins/xdg/util.py b/plugins/thumbnailer/icons/mixins/xdg/util.py similarity index 100% rename from src/solarfm/shellfm/windows/tabs/icons/mixins/xdg/util.py rename to plugins/thumbnailer/icons/mixins/xdg/util.py diff --git a/plugins/thumbnailer/manifest.json b/plugins/thumbnailer/manifest.json new file mode 100644 index 0000000..576316c --- /dev/null +++ b/plugins/thumbnailer/manifest.json @@ -0,0 +1,12 @@ +{ + "manifest": { + "name": "Thumbnailer", + "author": "ITDominator", + "version": "0.0.1", + "support": "", + "requests": { + "pass_fm_events": "true" + }, + "pre_launch": "true" + } +} diff --git a/plugins/thumbnailer/plugin.py b/plugins/thumbnailer/plugin.py new file mode 100644 index 0000000..7dd778f --- /dev/null +++ b/plugins/thumbnailer/plugin.py @@ -0,0 +1,59 @@ +# Python imports +import os + +# Lib imports + +# Application imports +from plugins.plugin_base import PluginBase +from .icons.controller import IconController + + + +class Plugin(PluginBase): + def __init__(self): + super().__init__() + + self.name = "Thumbnailer" # NOTE: Need to remove after establishing private bidirectional 1-1 message bus + # where self.name should not be needed for message comms + # self.path = os.path.dirname(os.path.realpath(__file__)) + + + def run(self): + self.icon_controller = IconController() + self._event_system.subscribe("create-thumbnail", self.create_thumbnail) + + def generate_reference_ui_element(self): + ... + + def create_thumbnail(self, dir, file) -> str: + return self.icon_controller.create_icon(dir, file) + + def get_video_icons(self, dir) -> list: + data = [] + + def get_video_icons(self) -> list: + data = [] + fvideos = self.icon_controller.fvideos + vids = [ file for file in os.path.list_dir(dir) if file.lower().endswith(fvideos) ] + + for file in vids: + img_hash, hash_img_path = self.create_video_thumbnail(full_path = f"{dir}/{file}", returnHashInstead = True) + data.append([img_hash, hash_img_path]) + + return data + + def get_pixbuf_icon_str_combo(self, dir) -> list: + data = [] + for file in os.path.list_dir(dir): + icon = self.icon_controller.create_icon(dir, file).get_pixbuf() + data.append([icon, file]) + + return data + + def get_gtk_icon_str_combo(self, dir) -> list: + data = [] + for file in os.path.list_dir(dir): + icon = self.icon_controller.create_icon(dir, file) + data.append([icon, file[0]]) + + return data diff --git a/plugins/thumbnailer/settings.json b/plugins/thumbnailer/settings.json new file mode 100644 index 0000000..134ca82 --- /dev/null +++ b/plugins/thumbnailer/settings.json @@ -0,0 +1,101 @@ +{ + "config":{ + "thumbnailer_path":"ffmpegthumbnailer", + "blender_thumbnailer_path":"", + "container_icon_wh":[ + 128, + 128 + ], + "video_icon_wh":[ + 128, + 64 + ], + "sys_icon_wh":[ + 56, + 56 + ], + "steam_cdn_url":"https://steamcdn-a.akamaihd.net/steam/apps/", + "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" + ], + "images":[ + ".png", + ".jpg", + ".jpeg", + ".gif", + ".ico", + ".tga", + ".webp" + ], + "text":[ + ".txt", + ".text", + ".sh", + ".cfg", + ".conf", + ".log" + ], + "music":[ + ".psf", + ".mp3", + ".ogg", + ".flac", + ".m4a" + ], + "pdf":[ + ".pdf" + ] + } +} diff --git a/plugins/trasher/plugin.py b/plugins/trasher/plugin.py index fd71ecf..dd0e81e 100644 --- a/plugins/trasher/plugin.py +++ b/plugins/trasher/plugin.py @@ -111,6 +111,8 @@ class Plugin(PluginBase): for uri in state.uris: self.trashman.trash(uri, verbocity) + self.trashman.regenerate() + def restore_trash_files(self, widget = None, eve = None, verbocity = False): self._event_system.emit("get_current_state") state = self._fm_state diff --git a/plugins/trasher/trash.py b/plugins/trasher/trash.py index 4210f9c..d60d086 100755 --- a/plugins/trasher/trash.py +++ b/plugins/trasher/trash.py @@ -43,4 +43,4 @@ class Trash(object): def restore(self, filename, verbose): """Restore a file from trash.""" - raise NotImplementedError(_('Backend didn’t \ implement this functionality')) + raise NotImplementedError(_('Backend didn’t implement this functionality')) diff --git a/plugins/trasher/xdgtrash.py b/plugins/trasher/xdgtrash.py index 02ff013..0ff2cf4 100755 --- a/plugins/trasher/xdgtrash.py +++ b/plugins/trasher/xdgtrash.py @@ -127,7 +127,7 @@ DeletionDate={} f.write(infofile) f.close() - self.regenerate() + # self.regenerate() if verbose: sys.stderr.write(_('trashed \'{}\'\n').format(filename)) diff --git a/src/solarfm/core/controller.py b/src/solarfm/core/controller.py index 31f1bee..79b0796 100644 --- a/src/solarfm/core/controller.py +++ b/src/solarfm/core/controller.py @@ -44,10 +44,13 @@ class Controller(UIMixin, SignalsMixins, Controller_Data): self._subscribe_to_events() self._load_widgets() + if args.no_plugins == "false": + self.plugins_controller.pre_launch_plugins() + self._generate_file_views(self.fm_controller_data) if args.no_plugins == "false": - self.plugins.launch_plugins() + self.plugins_controller.post_launch_plugins() for arg in unknownargs + [args.new_tab,]: if os.path.isdir(arg): @@ -116,7 +119,7 @@ class Controller(UIMixin, SignalsMixins, Controller_Data): def reload_plugins(self, widget=None, eve=None): - self.plugins.reload_plugins() + self.plugins_controller.reload_plugins() def do_action_from_menu_controls(self, _action=None, eve=None): @@ -196,4 +199,4 @@ class Controller(UIMixin, SignalsMixins, Controller_Data): tab.execute([f"{tab.terminal_app}"], start_dir=tab.get_current_directory()) def go_to_path(self, path: str): - self.builder.get_object("path_entry").set_text(path) \ No newline at end of file + self.builder.get_object("path_entry").set_text(path) diff --git a/src/solarfm/core/controller_data.py b/src/solarfm/core/controller_data.py index 7a1514d..da7b7b0 100644 --- a/src/solarfm/core/controller_data.py +++ b/src/solarfm/core/controller_data.py @@ -29,7 +29,7 @@ class Controller_Data: self._load_glade_file() self.fm_controller = WindowController() - self.plugins = PluginsController() + self.plugins_controller = PluginsController() self.fm_controller_data = self.fm_controller.get_state_from_file() self.window1 = self.builder.get_object("window_1") @@ -179,4 +179,4 @@ class Controller_Data: proc = subprocess.Popen(['xclip','-selection','clipboard'], stdin=subprocess.PIPE) proc.stdin.write(data.encode("utf-8")) proc.stdin.close() - retcode = proc.wait() \ No newline at end of file + retcode = proc.wait() diff --git a/src/solarfm/core/mixins/ui/grid_mixin.py b/src/solarfm/core/mixins/ui/grid_mixin.py index 0a36c09..c0c9948 100644 --- a/src/solarfm/core/mixins/ui/grid_mixin.py +++ b/src/solarfm/core/mixins/ui/grid_mixin.py @@ -31,7 +31,6 @@ class GridMixin: # return - dir = tab.get_current_directory() files = tab.get_files() @@ -167,4 +166,4 @@ class GridMixin: icon_grid = obj.get_children()[0] name = icon_grid.get_name() if name == _name: - return icon_grid \ No newline at end of file + return icon_grid diff --git a/src/solarfm/core/ui_mixin.py b/src/solarfm/core/ui_mixin.py index cbca606..0c04c7b 100644 --- a/src/solarfm/core/ui_mixin.py +++ b/src/solarfm/core/ui_mixin.py @@ -86,4 +86,4 @@ class UIMixin(PaneMixin, WindowMixin): for j in range(0, 4): i = j + 1 self.fm_controller.create_window() - self.create_new_tab_notebook(None, i, None) \ No newline at end of file + self.create_new_tab_notebook(None, i, None) diff --git a/src/solarfm/plugins/manifest.py b/src/solarfm/plugins/manifest.py index f7dc613..bfcebc7 100644 --- a/src/solarfm/plugins/manifest.py +++ b/src/solarfm/plugins/manifest.py @@ -15,32 +15,37 @@ class ManifestProcessorException(Exception): ... -@dataclass(slots=True) +@dataclass(slots = True) class PluginInfo: - path: str = None - name: str = None - author: str = None - version: str = None - support: str = None - requests:{} = None - reference: type = None + path: str = None + name: str = None + author: str = None + version: str = None + support: str = None + requests:{} = None + reference: type = None + pre_launch: bool = False class ManifestProcessor: def __init__(self, path, builder): - manifest = join(path, "manifest.json") - if not os.path.exists(manifest): + manifest_pth = join(path, "manifest.json") + if not os.path.exists(manifest_pth): raise ManifestProcessorException("Invalid Plugin Structure: Plugin doesn't have 'manifest.json'. Aboarting load...") self._path = path self._builder = builder - with open(manifest) as f: + with open(manifest_pth) as f: data = json.load(f) self._manifest = data["manifest"] self._plugin = self.collect_info() + def is_pre_launch(self) -> bool: + return self._plugin.pre_launch + def collect_info(self) -> PluginInfo: plugin = PluginInfo() + plugin.path = self._path plugin.name = self._manifest["name"] plugin.author = self._manifest["author"] @@ -48,6 +53,9 @@ class ManifestProcessor: plugin.support = self._manifest["support"] plugin.requests = self._manifest["requests"] + if "pre_launch" in self._manifest.keys(): + plugin.pre_launch = True if self._manifest["pre_launch"] == "true" else False + return plugin def get_loading_data(self): @@ -90,4 +98,4 @@ class ManifestProcessor: if isinstance(requests["bind_keys"], list): loading_data["bind_keys"] = requests["bind_keys"] - return self._plugin, loading_data \ No newline at end of file + return self._plugin, loading_data diff --git a/src/solarfm/plugins/plugins_controller.py b/src/solarfm/plugins/plugins_controller.py index f37cffc..07d4b72 100644 --- a/src/solarfm/plugins/plugins_controller.py +++ b/src/solarfm/plugins/plugins_controller.py @@ -36,41 +36,76 @@ class PluginsController: self._plugins_dir_watcher = None self._plugin_collection = [] + self._plugin_manifests = {} + + self._load_manifests() - def launch_plugins(self) -> None: + def _load_manifests(self): + logger.info(f"Loading manifests...") + + for path, folder in [[join(self._plugins_path, item), item] if os.path.isdir(join(self._plugins_path, item)) else None for item in os.listdir(self._plugins_path)]: + manifest = ManifestProcessor(path, self._builder) + self._plugin_manifests[path] = { + "path": path, + "folder": folder, + "manifest": manifest + } + self._set_plugins_watcher() - self.load_plugins() def _set_plugins_watcher(self) -> None: self._plugins_dir_watcher = Gio.File.new_for_path(self._plugins_path) \ .monitor_directory(Gio.FileMonitorFlags.WATCH_MOVES, Gio.Cancellable()) self._plugins_dir_watcher.connect("changed", self._on_plugins_changed, ()) - def _on_plugins_changed(self, file_monitor, file, other_file=None, eve_type=None, data=None): + def _on_plugins_changed(self, file_monitor, file, other_file = None, eve_type = None, data = None): if eve_type in [Gio.FileMonitorEvent.CREATED, Gio.FileMonitorEvent.DELETED, Gio.FileMonitorEvent.RENAMED, Gio.FileMonitorEvent.MOVED_IN, Gio.FileMonitorEvent.MOVED_OUT]: self.reload_plugins(file) - @daemon_threaded - def load_plugins(self, file: str = None) -> None: - logger.info(f"Loading plugins...") + def pre_launch_plugins(self) -> None: + logger.info(f"Loading pre-launch plugins...") + plugin_manifests: {} = {} + + for key in self._plugin_manifests: + target_manifest = self._plugin_manifests[key]["manifest"] + if target_manifest.is_pre_launch(): + plugin_manifests[key] = self._plugin_manifests[key] + + self._load_plugins(plugin_manifests, is_pre_launch = True) + + def post_launch_plugins(self) -> None: + logger.info(f"Loading post-launch plugins...") + plugin_manifests: {} = {} + + for key in self._plugin_manifests: + target_manifest = self._plugin_manifests[key]["manifest"] + if not target_manifest.is_pre_launch(): + plugin_manifests[key] = self._plugin_manifests[key] + + self._load_plugins(plugin_manifests) + + def _load_plugins(self, plugin_manifests: {} = {}, is_pre_launch: bool = False) -> None: parent_path = os.getcwd() - for path, folder in [[join(self._plugins_path, item), item] if os.path.isdir(join(self._plugins_path, item)) else None for item in os.listdir(self._plugins_path)]: - try: - target = join(path, "plugin.py") - manifest = ManifestProcessor(path, self._builder) + for key in plugin_manifests: + target_manifest = plugin_manifests[key] + path, folder, manifest = target_manifest["path"], target_manifest["folder"], target_manifest["manifest"] + try: + target = join(path, "plugin.py") if not os.path.exists(target): raise FileNotFoundError("Invalid Plugin Structure: Plugin doesn't have 'plugin.py'. Aboarting load...") plugin, loading_data = manifest.get_loading_data() module = self.load_plugin_module(path, folder, target) - GLib.idle_add(self.execute_plugin, *(module, plugin, loading_data)) - # self.execute_plugin(module, plugin, loading_data) + if is_pre_launch: + self.execute_plugin(module, plugin, loading_data) + else: + GLib.idle_add(self.execute_plugin, *(module, plugin, loading_data)) except InvalidPluginException as e: logger.info(f"Malformed Plugin: Not loading -->: '{folder}' !") logger.debug("Trace: ", traceback.print_exc()) @@ -128,4 +163,4 @@ class PluginsController: os.chdir(plugin.path) plugin.reference.reload_package(f"{plugin.path}/plugin.py") - os.chdir(parent_path) \ No newline at end of file + os.chdir(parent_path) diff --git a/src/solarfm/shellfm/windows/tabs/tab.py b/src/solarfm/shellfm/windows/tabs/tab.py index d450421..f51ea5d 100644 --- a/src/solarfm/shellfm/windows/tabs/tab.py +++ b/src/solarfm/shellfm/windows/tabs/tab.py @@ -14,7 +14,6 @@ from random import randint from .utils.settings import Settings from .utils.launcher import Launcher from .utils.filehandler import FileHandler -from .icons.icon import Icon from .path import Path @@ -40,9 +39,8 @@ except Exception as e: -class Tab(Settings, FileHandler, Launcher, Icon, Path): +class Tab(Settings, FileHandler, Launcher, Path): def __init__(self): - self.logger = None self._id_length: int = 10 self._id: str = "" @@ -168,33 +166,6 @@ class Tab(Settings, FileHandler, Launcher, Icon, Path): } } - def get_video_icons(self) -> list: - data = [] - dir = self.get_current_directory() - for file in self._vids: - img_hash, hash_img_path = self.create_video_thumbnail(full_path=f"{dir}/{file}", returnHashInstead=True) - data.append([img_hash, hash_img_path]) - - return data - - def get_pixbuf_icon_str_combo(self): - data = [] - dir = self.get_current_directory() - for file in self._files: - icon = self.create_icon(dir, file).get_pixbuf() - data.append([icon, file]) - - return data - - def get_gtk_icon_str_combo(self) -> list: - data = [] - dir = self.get_current_directory() - for file in self._files: - icon = self.create_icon(dir, file) - data.append([icon, file[0]]) - - return data - def get_current_directory(self) -> str: return self.get_path() @@ -264,7 +235,7 @@ class Tab(Settings, FileHandler, Launcher, Icon, Path): return int(text) if text.isdigit() else text def _natural_keys(self, text): - return [ self._atoi(c) for c in re.split('(\d+)',text) ] + return [ self._atoi(c) for c in re.split(r'(\d+)', text) ] def _hash_text(self, text) -> str: return hashlib.sha256(str.encode(text)).hexdigest()[:18] @@ -289,3 +260,7 @@ class Tab(Settings, FileHandler, Launcher, Icon, Path): def _set_error_message(self, text: str): self.error_message = text + + + def create_icon(self, dir, file): + return event_system.emit_and_await("create-thumbnail", (dir, file,)) diff --git a/src/solarfm/shellfm/windows/tabs/utils/launcher.py b/src/solarfm/shellfm/windows/tabs/utils/launcher.py index a7febc3..6e07b4b 100644 --- a/src/solarfm/shellfm/windows/tabs/utils/launcher.py +++ b/src/solarfm/shellfm/windows/tabs/utils/launcher.py @@ -41,11 +41,11 @@ class Launcher: def execute(self, command, start_dir=os.getenv("HOME"), use_shell=False): try: - self.logger.debug(command) + logger.debug(command) subprocess.Popen(command, cwd=start_dir, shell=use_shell, start_new_session=True, stdout=None, stderr=None, close_fds=True) except ShellFMLauncherException as e: - self.logger.error(f"Couldn't execute: {command}") - self.logger.error(e) + logger.error(f"Couldn't execute: {command}") + logger.error(e) # TODO: Return std(out/in/err) handlers along with subprocess instead of sinking to null def execute_and_return_thread_handler(self, command, start_dir=os.getenv("HOME"), use_shell=False): @@ -53,8 +53,8 @@ class Launcher: DEVNULL = open(os.devnull, 'w') return subprocess.Popen(command, cwd=start_dir, shell=use_shell, start_new_session=False, stdout=DEVNULL, stderr=DEVNULL, close_fds=False) except ShellFMLauncherException as e: - self.logger.error(f"Couldn't execute and return thread: {command}") - self.logger.error(e) + logger.error(f"Couldn't execute and return thread: {command}") + logger.error(e) return None @threaded @@ -63,7 +63,7 @@ class Launcher: def remux_video(self, hash, file): remux_vid_pth = "{self.REMUX_FOLDER}/{hash}.mp4" - self.logger.debug(remux_vid_pth) + logger.debug(remux_vid_pth) if not os.path.isfile(remux_vid_pth): self.check_remux_space() @@ -83,8 +83,8 @@ class Launcher: proc = subprocess.Popen(command) proc.wait() except ShellFMLauncherException as e: - self.logger.error(message) - self.logger.error(e) + logger.error(message) + logger.error(e) return False return True @@ -94,7 +94,7 @@ class Launcher: try: limit = int(limit) except ShellFMLauncherException as e: - self.logger.debug(e) + logger.debug(e) return usage = self.get_remux_folder_usage(self.REMUX_FOLDER) @@ -113,4 +113,4 @@ class Launcher: if not os.path.islink(fp): # Skip if it is symbolic link total_size += os.path.getsize(fp) - return total_size \ No newline at end of file + return total_size diff --git a/src/solarfm/shellfm/windows/tabs/utils/settings.py b/src/solarfm/shellfm/windows/tabs/utils/settings.py index f3c0cd2..3be7131 100644 --- a/src/solarfm/shellfm/windows/tabs/utils/settings.py +++ b/src/solarfm/shellfm/windows/tabs/utils/settings.py @@ -14,8 +14,6 @@ class ShellFMSettingsException(Exception): class Settings: - logger = None - # NOTE: app_name should be defined using python 'builtins' app_name_exists = False try: @@ -31,45 +29,13 @@ class Settings: CONFIG_FILE = f"{CONFIG_PATH}/settings.json" HIDE_HIDDEN_FILES = True - DEFAULT_ICONS = f"{CONFIG_PATH}/icons" - DEFAULT_ICON = f"{DEFAULT_ICONS}/text.png" - 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" - ABS_THUMBS_PTH = f"{BASE_THUMBS_PTH}/normal" - STEAM_ICONS_PTH = f"{BASE_THUMBS_PTH}/steam_icons" - - if not os.path.exists(CONFIG_PATH) or not os.path.exists(CONFIG_FILE): - msg = f"No config file located! Aborting loading ShellFM library...\nExpected: {CONFIG_FILE}" - raise ShellFMSettingsException(msg) - - if not path.isdir(REMUX_FOLDER): - os.mkdir(REMUX_FOLDER) - - if not path.isdir(BASE_THUMBS_PTH): - os.mkdir(BASE_THUMBS_PTH) - - if not path.isdir(ABS_THUMBS_PTH): - os.mkdir(ABS_THUMBS_PTH) - - if not path.isdir(STEAM_ICONS_PTH): - os.mkdir(STEAM_ICONS_PTH) - - if not os.path.exists(DEFAULT_ICONS): - DEFAULT_ICONS = f"{USR_APP_CONTEXT}/icons" - DEFAULT_ICON = f"{DEFAULT_ICONS}/text.png" - with open(CONFIG_FILE) as f: settings = json.load(f) config = settings["config"] subpath = config["base_of_home"] - STEAM_CDN_URL = config["steam_cdn_url"] - 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"] in ["true", ""] else False go_past_home = True if config["go_past_home"] in ["true", ""] else False lock_folder = False if config["lock_folder"] in ["false", ""] else True @@ -83,9 +49,6 @@ class Settings: code_app = config["code_app"] text_app = config["text_app"] terminal_app = config["terminal_app"] - container_icon_wh = config["container_icon_wh"] - video_icon_wh = config["video_icon_wh"] - sys_icon_wh = config["sys_icon_wh"] file_manager_app = config["file_manager_app"] remux_folder_max_disk_usage = config["remux_folder_max_disk_usage"] diff --git a/user_config/usr/share/solarfm/settings.json b/user_config/usr/share/solarfm/settings.json index 8627762..1cd962f 100644 --- a/user_config/usr/share/solarfm/settings.json +++ b/user_config/usr/share/solarfm/settings.json @@ -2,8 +2,6 @@ "config": { "base_of_home": "", "hide_hidden_files": "true", - "thumbnailer_path": "ffmpegthumbnailer", - "blender_thumbnailer_path": "", "go_past_home": "true", "lock_folder": "false", "locked_folders": "venv::::flasks", @@ -16,11 +14,7 @@ "code_app": "newton", "text_app": "mousepad", "terminal_app": "terminator", - "container_icon_wh": [128, 128], - "video_icon_wh": [128, 64], - "sys_icon_wh": [56, 56], "file_manager_app": "solarfm", - "steam_cdn_url": "https://steamcdn-a.akamaihd.net/steam/apps/", "remux_folder_max_disk_usage": "8589934592", "make_transparent":0, "main_window_x":721, @@ -29,6 +23,9 @@ "main_window_min_height":480, "main_window_width":800, "main_window_height":600, + "application_dirs":[ + "/usr/share/applications" + ] }, "filters": { "meshs": [".dae", ".fbx", ".gltf", ".obj", ".stl"], @@ -49,4 +46,4 @@ "ch_log_lvl": 20, "fh_log_lvl": 10 } -} \ No newline at end of file +}