moved thumbnail generation to plugin; extended plugin loading for pre and post window loading
This commit is contained in:
parent
2f954f4c79
commit
ce00970171
|
@ -14,6 +14,7 @@ class Manifest:
|
||||||
'ui_target': "plugin_control_list",
|
'ui_target': "plugin_control_list",
|
||||||
'pass_fm_events': "true"
|
'pass_fm_events': "true"
|
||||||
}
|
}
|
||||||
|
pre_launch: bool = False
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
"ui_target": "plugin_control_list",
|
"ui_target": "plugin_control_list",
|
||||||
"pass_fm_events": "true",
|
"pass_fm_events": "true",
|
||||||
"bind_keys": ["Example Plugin||send_message:<Control>f"]
|
"bind_keys": ["Example Plugin||send_message:<Control>f"]
|
||||||
}
|
},
|
||||||
|
"pre_launch": "false"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
"""
|
||||||
|
Pligin Module
|
||||||
|
"""
|
|
@ -0,0 +1,3 @@
|
||||||
|
"""
|
||||||
|
Pligin Package
|
||||||
|
"""
|
|
@ -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"])
|
|
@ -14,4 +14,4 @@ class MeshsIconMixin:
|
||||||
proc = subprocess.Popen([self.BLENDER_THUMBNLR, full_path, hash_img_path])
|
proc = subprocess.Popen([self.BLENDER_THUMBNLR, full_path, hash_img_path])
|
||||||
proc.wait()
|
proc.wait()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.debug(repr(e))
|
logger.debug(repr(e))
|
|
@ -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 = 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))
|
logger.info(repr(e))
|
||||||
self.ffprobe_generate_video_thumbnail(full_path, hash_img_path)
|
self.ffprobe_generate_video_thumbnail(full_path, hash_img_path)
|
||||||
|
|
||||||
|
|
||||||
|
@ -51,5 +51,4 @@ class VideoIconMixin:
|
||||||
proc.wait()
|
proc.wait()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Video thumbnail generation issue in thread:")
|
print("Video thumbnail generation issue in thread:")
|
||||||
print( repr(e) )
|
logger.info(repr(e))
|
||||||
self.logger.debug(repr(e))
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"manifest": {
|
||||||
|
"name": "Thumbnailer",
|
||||||
|
"author": "ITDominator",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"support": "",
|
||||||
|
"requests": {
|
||||||
|
"pass_fm_events": "true"
|
||||||
|
},
|
||||||
|
"pre_launch": "true"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -111,6 +111,8 @@ class Plugin(PluginBase):
|
||||||
for uri in state.uris:
|
for uri in state.uris:
|
||||||
self.trashman.trash(uri, verbocity)
|
self.trashman.trash(uri, verbocity)
|
||||||
|
|
||||||
|
self.trashman.regenerate()
|
||||||
|
|
||||||
def restore_trash_files(self, widget = None, eve = None, verbocity = False):
|
def restore_trash_files(self, widget = None, eve = None, verbocity = False):
|
||||||
self._event_system.emit("get_current_state")
|
self._event_system.emit("get_current_state")
|
||||||
state = self._fm_state
|
state = self._fm_state
|
||||||
|
|
|
@ -43,4 +43,4 @@ class Trash(object):
|
||||||
|
|
||||||
def restore(self, filename, verbose):
|
def restore(self, filename, verbose):
|
||||||
"""Restore a file from trash."""
|
"""Restore a file from trash."""
|
||||||
raise NotImplementedError(_('Backend didn’t \ implement this functionality'))
|
raise NotImplementedError(_('Backend didn’t implement this functionality'))
|
||||||
|
|
|
@ -127,7 +127,7 @@ DeletionDate={}
|
||||||
f.write(infofile)
|
f.write(infofile)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
self.regenerate()
|
# self.regenerate()
|
||||||
|
|
||||||
if verbose:
|
if verbose:
|
||||||
sys.stderr.write(_('trashed \'{}\'\n').format(filename))
|
sys.stderr.write(_('trashed \'{}\'\n').format(filename))
|
||||||
|
|
|
@ -44,10 +44,13 @@ class Controller(UIMixin, SignalsMixins, Controller_Data):
|
||||||
self._subscribe_to_events()
|
self._subscribe_to_events()
|
||||||
self._load_widgets()
|
self._load_widgets()
|
||||||
|
|
||||||
|
if args.no_plugins == "false":
|
||||||
|
self.plugins_controller.pre_launch_plugins()
|
||||||
|
|
||||||
self._generate_file_views(self.fm_controller_data)
|
self._generate_file_views(self.fm_controller_data)
|
||||||
|
|
||||||
if args.no_plugins == "false":
|
if args.no_plugins == "false":
|
||||||
self.plugins.launch_plugins()
|
self.plugins_controller.post_launch_plugins()
|
||||||
|
|
||||||
for arg in unknownargs + [args.new_tab,]:
|
for arg in unknownargs + [args.new_tab,]:
|
||||||
if os.path.isdir(arg):
|
if os.path.isdir(arg):
|
||||||
|
@ -116,7 +119,7 @@ class Controller(UIMixin, SignalsMixins, Controller_Data):
|
||||||
|
|
||||||
|
|
||||||
def reload_plugins(self, widget=None, eve=None):
|
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):
|
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())
|
tab.execute([f"{tab.terminal_app}"], start_dir=tab.get_current_directory())
|
||||||
|
|
||||||
def go_to_path(self, path: str):
|
def go_to_path(self, path: str):
|
||||||
self.builder.get_object("path_entry").set_text(path)
|
self.builder.get_object("path_entry").set_text(path)
|
||||||
|
|
|
@ -29,7 +29,7 @@ class Controller_Data:
|
||||||
|
|
||||||
self._load_glade_file()
|
self._load_glade_file()
|
||||||
self.fm_controller = WindowController()
|
self.fm_controller = WindowController()
|
||||||
self.plugins = PluginsController()
|
self.plugins_controller = PluginsController()
|
||||||
self.fm_controller_data = self.fm_controller.get_state_from_file()
|
self.fm_controller_data = self.fm_controller.get_state_from_file()
|
||||||
|
|
||||||
self.window1 = self.builder.get_object("window_1")
|
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 = subprocess.Popen(['xclip','-selection','clipboard'], stdin=subprocess.PIPE)
|
||||||
proc.stdin.write(data.encode("utf-8"))
|
proc.stdin.write(data.encode("utf-8"))
|
||||||
proc.stdin.close()
|
proc.stdin.close()
|
||||||
retcode = proc.wait()
|
retcode = proc.wait()
|
||||||
|
|
|
@ -31,7 +31,6 @@ class GridMixin:
|
||||||
|
|
||||||
# return
|
# return
|
||||||
|
|
||||||
|
|
||||||
dir = tab.get_current_directory()
|
dir = tab.get_current_directory()
|
||||||
files = tab.get_files()
|
files = tab.get_files()
|
||||||
|
|
||||||
|
@ -167,4 +166,4 @@ class GridMixin:
|
||||||
icon_grid = obj.get_children()[0]
|
icon_grid = obj.get_children()[0]
|
||||||
name = icon_grid.get_name()
|
name = icon_grid.get_name()
|
||||||
if name == _name:
|
if name == _name:
|
||||||
return icon_grid
|
return icon_grid
|
||||||
|
|
|
@ -86,4 +86,4 @@ class UIMixin(PaneMixin, WindowMixin):
|
||||||
for j in range(0, 4):
|
for j in range(0, 4):
|
||||||
i = j + 1
|
i = j + 1
|
||||||
self.fm_controller.create_window()
|
self.fm_controller.create_window()
|
||||||
self.create_new_tab_notebook(None, i, None)
|
self.create_new_tab_notebook(None, i, None)
|
||||||
|
|
|
@ -15,32 +15,37 @@ class ManifestProcessorException(Exception):
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
@dataclass(slots=True)
|
@dataclass(slots = True)
|
||||||
class PluginInfo:
|
class PluginInfo:
|
||||||
path: str = None
|
path: str = None
|
||||||
name: str = None
|
name: str = None
|
||||||
author: str = None
|
author: str = None
|
||||||
version: str = None
|
version: str = None
|
||||||
support: str = None
|
support: str = None
|
||||||
requests:{} = None
|
requests:{} = None
|
||||||
reference: type = None
|
reference: type = None
|
||||||
|
pre_launch: bool = False
|
||||||
|
|
||||||
|
|
||||||
class ManifestProcessor:
|
class ManifestProcessor:
|
||||||
def __init__(self, path, builder):
|
def __init__(self, path, builder):
|
||||||
manifest = join(path, "manifest.json")
|
manifest_pth = join(path, "manifest.json")
|
||||||
if not os.path.exists(manifest):
|
if not os.path.exists(manifest_pth):
|
||||||
raise ManifestProcessorException("Invalid Plugin Structure: Plugin doesn't have 'manifest.json'. Aboarting load...")
|
raise ManifestProcessorException("Invalid Plugin Structure: Plugin doesn't have 'manifest.json'. Aboarting load...")
|
||||||
|
|
||||||
self._path = path
|
self._path = path
|
||||||
self._builder = builder
|
self._builder = builder
|
||||||
with open(manifest) as f:
|
with open(manifest_pth) as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
self._manifest = data["manifest"]
|
self._manifest = data["manifest"]
|
||||||
self._plugin = self.collect_info()
|
self._plugin = self.collect_info()
|
||||||
|
|
||||||
|
def is_pre_launch(self) -> bool:
|
||||||
|
return self._plugin.pre_launch
|
||||||
|
|
||||||
def collect_info(self) -> PluginInfo:
|
def collect_info(self) -> PluginInfo:
|
||||||
plugin = PluginInfo()
|
plugin = PluginInfo()
|
||||||
|
|
||||||
plugin.path = self._path
|
plugin.path = self._path
|
||||||
plugin.name = self._manifest["name"]
|
plugin.name = self._manifest["name"]
|
||||||
plugin.author = self._manifest["author"]
|
plugin.author = self._manifest["author"]
|
||||||
|
@ -48,6 +53,9 @@ class ManifestProcessor:
|
||||||
plugin.support = self._manifest["support"]
|
plugin.support = self._manifest["support"]
|
||||||
plugin.requests = self._manifest["requests"]
|
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
|
return plugin
|
||||||
|
|
||||||
def get_loading_data(self):
|
def get_loading_data(self):
|
||||||
|
@ -90,4 +98,4 @@ class ManifestProcessor:
|
||||||
if isinstance(requests["bind_keys"], list):
|
if isinstance(requests["bind_keys"], list):
|
||||||
loading_data["bind_keys"] = requests["bind_keys"]
|
loading_data["bind_keys"] = requests["bind_keys"]
|
||||||
|
|
||||||
return self._plugin, loading_data
|
return self._plugin, loading_data
|
||||||
|
|
|
@ -36,41 +36,76 @@ class PluginsController:
|
||||||
|
|
||||||
self._plugins_dir_watcher = None
|
self._plugins_dir_watcher = None
|
||||||
self._plugin_collection = []
|
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._set_plugins_watcher()
|
||||||
self.load_plugins()
|
|
||||||
|
|
||||||
def _set_plugins_watcher(self) -> None:
|
def _set_plugins_watcher(self) -> None:
|
||||||
self._plugins_dir_watcher = Gio.File.new_for_path(self._plugins_path) \
|
self._plugins_dir_watcher = Gio.File.new_for_path(self._plugins_path) \
|
||||||
.monitor_directory(Gio.FileMonitorFlags.WATCH_MOVES, Gio.Cancellable())
|
.monitor_directory(Gio.FileMonitorFlags.WATCH_MOVES, Gio.Cancellable())
|
||||||
self._plugins_dir_watcher.connect("changed", self._on_plugins_changed, ())
|
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,
|
if eve_type in [Gio.FileMonitorEvent.CREATED, Gio.FileMonitorEvent.DELETED,
|
||||||
Gio.FileMonitorEvent.RENAMED, Gio.FileMonitorEvent.MOVED_IN,
|
Gio.FileMonitorEvent.RENAMED, Gio.FileMonitorEvent.MOVED_IN,
|
||||||
Gio.FileMonitorEvent.MOVED_OUT]:
|
Gio.FileMonitorEvent.MOVED_OUT]:
|
||||||
self.reload_plugins(file)
|
self.reload_plugins(file)
|
||||||
|
|
||||||
@daemon_threaded
|
def pre_launch_plugins(self) -> None:
|
||||||
def load_plugins(self, file: str = None) -> None:
|
logger.info(f"Loading pre-launch plugins...")
|
||||||
logger.info(f"Loading 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()
|
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)]:
|
for key in plugin_manifests:
|
||||||
try:
|
target_manifest = plugin_manifests[key]
|
||||||
target = join(path, "plugin.py")
|
path, folder, manifest = target_manifest["path"], target_manifest["folder"], target_manifest["manifest"]
|
||||||
manifest = ManifestProcessor(path, self._builder)
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
target = join(path, "plugin.py")
|
||||||
if not os.path.exists(target):
|
if not os.path.exists(target):
|
||||||
raise FileNotFoundError("Invalid Plugin Structure: Plugin doesn't have 'plugin.py'. Aboarting load...")
|
raise FileNotFoundError("Invalid Plugin Structure: Plugin doesn't have 'plugin.py'. Aboarting load...")
|
||||||
|
|
||||||
plugin, loading_data = manifest.get_loading_data()
|
plugin, loading_data = manifest.get_loading_data()
|
||||||
module = self.load_plugin_module(path, folder, target)
|
module = self.load_plugin_module(path, folder, target)
|
||||||
|
|
||||||
GLib.idle_add(self.execute_plugin, *(module, plugin, loading_data))
|
if is_pre_launch:
|
||||||
# self.execute_plugin(module, plugin, loading_data)
|
self.execute_plugin(module, plugin, loading_data)
|
||||||
|
else:
|
||||||
|
GLib.idle_add(self.execute_plugin, *(module, plugin, loading_data))
|
||||||
except InvalidPluginException as e:
|
except InvalidPluginException as e:
|
||||||
logger.info(f"Malformed Plugin: Not loading -->: '{folder}' !")
|
logger.info(f"Malformed Plugin: Not loading -->: '{folder}' !")
|
||||||
logger.debug("Trace: ", traceback.print_exc())
|
logger.debug("Trace: ", traceback.print_exc())
|
||||||
|
@ -128,4 +163,4 @@ class PluginsController:
|
||||||
os.chdir(plugin.path)
|
os.chdir(plugin.path)
|
||||||
plugin.reference.reload_package(f"{plugin.path}/plugin.py")
|
plugin.reference.reload_package(f"{plugin.path}/plugin.py")
|
||||||
|
|
||||||
os.chdir(parent_path)
|
os.chdir(parent_path)
|
||||||
|
|
|
@ -14,7 +14,6 @@ from random import randint
|
||||||
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 .path import Path
|
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):
|
def __init__(self):
|
||||||
self.logger = None
|
|
||||||
self._id_length: int = 10
|
self._id_length: int = 10
|
||||||
|
|
||||||
self._id: str = ""
|
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:
|
def get_current_directory(self) -> str:
|
||||||
return self.get_path()
|
return self.get_path()
|
||||||
|
|
||||||
|
@ -264,7 +235,7 @@ class Tab(Settings, FileHandler, Launcher, Icon, Path):
|
||||||
return int(text) if text.isdigit() else text
|
return int(text) if text.isdigit() else text
|
||||||
|
|
||||||
def _natural_keys(self, 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:
|
def _hash_text(self, text) -> str:
|
||||||
return hashlib.sha256(str.encode(text)).hexdigest()[:18]
|
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):
|
def _set_error_message(self, text: str):
|
||||||
self.error_message = text
|
self.error_message = text
|
||||||
|
|
||||||
|
|
||||||
|
def create_icon(self, dir, file):
|
||||||
|
return event_system.emit_and_await("create-thumbnail", (dir, file,))
|
||||||
|
|
|
@ -41,11 +41,11 @@ class Launcher:
|
||||||
|
|
||||||
def execute(self, command, start_dir=os.getenv("HOME"), use_shell=False):
|
def execute(self, command, start_dir=os.getenv("HOME"), use_shell=False):
|
||||||
try:
|
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)
|
subprocess.Popen(command, cwd=start_dir, shell=use_shell, start_new_session=True, stdout=None, stderr=None, close_fds=True)
|
||||||
except ShellFMLauncherException as e:
|
except ShellFMLauncherException as e:
|
||||||
self.logger.error(f"Couldn't execute: {command}")
|
logger.error(f"Couldn't execute: {command}")
|
||||||
self.logger.error(e)
|
logger.error(e)
|
||||||
|
|
||||||
# TODO: Return std(out/in/err) handlers along with subprocess instead of sinking to null
|
# 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):
|
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')
|
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)
|
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:
|
except ShellFMLauncherException as e:
|
||||||
self.logger.error(f"Couldn't execute and return thread: {command}")
|
logger.error(f"Couldn't execute and return thread: {command}")
|
||||||
self.logger.error(e)
|
logger.error(e)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@threaded
|
@threaded
|
||||||
|
@ -63,7 +63,7 @@ class Launcher:
|
||||||
|
|
||||||
def remux_video(self, hash, file):
|
def remux_video(self, hash, file):
|
||||||
remux_vid_pth = "{self.REMUX_FOLDER}/{hash}.mp4"
|
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):
|
if not os.path.isfile(remux_vid_pth):
|
||||||
self.check_remux_space()
|
self.check_remux_space()
|
||||||
|
@ -83,8 +83,8 @@ class Launcher:
|
||||||
proc = subprocess.Popen(command)
|
proc = subprocess.Popen(command)
|
||||||
proc.wait()
|
proc.wait()
|
||||||
except ShellFMLauncherException as e:
|
except ShellFMLauncherException as e:
|
||||||
self.logger.error(message)
|
logger.error(message)
|
||||||
self.logger.error(e)
|
logger.error(e)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
@ -94,7 +94,7 @@ class Launcher:
|
||||||
try:
|
try:
|
||||||
limit = int(limit)
|
limit = int(limit)
|
||||||
except ShellFMLauncherException as e:
|
except ShellFMLauncherException as e:
|
||||||
self.logger.debug(e)
|
logger.debug(e)
|
||||||
return
|
return
|
||||||
|
|
||||||
usage = self.get_remux_folder_usage(self.REMUX_FOLDER)
|
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
|
if not os.path.islink(fp): # Skip if it is symbolic link
|
||||||
total_size += os.path.getsize(fp)
|
total_size += os.path.getsize(fp)
|
||||||
|
|
||||||
return total_size
|
return total_size
|
||||||
|
|
|
@ -14,8 +14,6 @@ class ShellFMSettingsException(Exception):
|
||||||
|
|
||||||
|
|
||||||
class Settings:
|
class Settings:
|
||||||
logger = None
|
|
||||||
|
|
||||||
# NOTE: app_name should be defined using python 'builtins'
|
# NOTE: app_name should be defined using python 'builtins'
|
||||||
app_name_exists = False
|
app_name_exists = False
|
||||||
try:
|
try:
|
||||||
|
@ -31,45 +29,13 @@ class Settings:
|
||||||
CONFIG_FILE = f"{CONFIG_PATH}/settings.json"
|
CONFIG_FILE = f"{CONFIG_PATH}/settings.json"
|
||||||
HIDE_HIDDEN_FILES = True
|
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
|
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:
|
with open(CONFIG_FILE) as f:
|
||||||
settings = json.load(f)
|
settings = json.load(f)
|
||||||
config = settings["config"]
|
config = settings["config"]
|
||||||
|
|
||||||
subpath = config["base_of_home"]
|
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
|
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
|
go_past_home = True if config["go_past_home"] in ["true", ""] else False
|
||||||
lock_folder = False if config["lock_folder"] in ["false", ""] else True
|
lock_folder = False if config["lock_folder"] in ["false", ""] else True
|
||||||
|
@ -83,9 +49,6 @@ class Settings:
|
||||||
code_app = config["code_app"]
|
code_app = config["code_app"]
|
||||||
text_app = config["text_app"]
|
text_app = config["text_app"]
|
||||||
terminal_app = config["terminal_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"]
|
file_manager_app = config["file_manager_app"]
|
||||||
remux_folder_max_disk_usage = config["remux_folder_max_disk_usage"]
|
remux_folder_max_disk_usage = config["remux_folder_max_disk_usage"]
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
"config": {
|
"config": {
|
||||||
"base_of_home": "",
|
"base_of_home": "",
|
||||||
"hide_hidden_files": "true",
|
"hide_hidden_files": "true",
|
||||||
"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",
|
||||||
|
@ -16,11 +14,7 @@
|
||||||
"code_app": "newton",
|
"code_app": "newton",
|
||||||
"text_app": "mousepad",
|
"text_app": "mousepad",
|
||||||
"terminal_app": "terminator",
|
"terminal_app": "terminator",
|
||||||
"container_icon_wh": [128, 128],
|
|
||||||
"video_icon_wh": [128, 64],
|
|
||||||
"sys_icon_wh": [56, 56],
|
|
||||||
"file_manager_app": "solarfm",
|
"file_manager_app": "solarfm",
|
||||||
"steam_cdn_url": "https://steamcdn-a.akamaihd.net/steam/apps/",
|
|
||||||
"remux_folder_max_disk_usage": "8589934592",
|
"remux_folder_max_disk_usage": "8589934592",
|
||||||
"make_transparent":0,
|
"make_transparent":0,
|
||||||
"main_window_x":721,
|
"main_window_x":721,
|
||||||
|
@ -29,6 +23,9 @@
|
||||||
"main_window_min_height":480,
|
"main_window_min_height":480,
|
||||||
"main_window_width":800,
|
"main_window_width":800,
|
||||||
"main_window_height":600,
|
"main_window_height":600,
|
||||||
|
"application_dirs":[
|
||||||
|
"/usr/share/applications"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"filters": {
|
"filters": {
|
||||||
"meshs": [".dae", ".fbx", ".gltf", ".obj", ".stl"],
|
"meshs": [".dae", ".fbx", ".gltf", ".obj", ".stl"],
|
||||||
|
@ -49,4 +46,4 @@
|
||||||
"ch_log_lvl": 20,
|
"ch_log_lvl": 20,
|
||||||
"fh_log_lvl": 10
|
"fh_log_lvl": 10
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue