From 7abbee91824debdeb256d47e3e9c46132fd6f2f7 Mon Sep 17 00:00:00 2001 From: itdominator <1itdominator@gmail.com> Date: Tue, 19 Jul 2022 16:46:38 -0500 Subject: [PATCH 1/4] Addec compilation to binary using Nuitka --- .../solarfm-0.0.1/SolarFM/create-binary.sh | 17 +++++++++++++++++ .../{solarfm.sh => create-standalone.sh} | 7 +++---- .../SolarFM/solarfm/core/controller_data.py | 2 +- 3 files changed, 21 insertions(+), 5 deletions(-) create mode 100755 src/versions/solarfm-0.0.1/SolarFM/create-binary.sh rename src/versions/solarfm-0.0.1/SolarFM/{solarfm.sh => create-standalone.sh} (50%) diff --git a/src/versions/solarfm-0.0.1/SolarFM/create-binary.sh b/src/versions/solarfm-0.0.1/SolarFM/create-binary.sh new file mode 100755 index 0000000..2cd29cc --- /dev/null +++ b/src/versions/solarfm-0.0.1/SolarFM/create-binary.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# . CONFIG.sh + +# set -o xtrace ## To debug scripts +# set -o errexit ## To exit on error +# set -o errunset ## To exit if a variable is referenced but not set + + +function main() { + cd "$(dirname "")" + echo "Working Dir: " $(pwd) + + source "/home/abaddon/Portable_Apps/py-venvs/gtk-apps-venv/venv/bin/activate" + python -m nuitka --onefile --follow-imports --linux-onefile-icon="/home/abaddon/.config/solarfm/solarfm.png" solarfm/__main__.py -o solarfm.a +} +main "$@"; diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm.sh b/src/versions/solarfm-0.0.1/SolarFM/create-standalone.sh similarity index 50% rename from src/versions/solarfm-0.0.1/SolarFM/solarfm.sh rename to src/versions/solarfm-0.0.1/SolarFM/create-standalone.sh index 67de002..ccc97e0 100755 --- a/src/versions/solarfm-0.0.1/SolarFM/solarfm.sh +++ b/src/versions/solarfm-0.0.1/SolarFM/create-standalone.sh @@ -8,11 +8,10 @@ function main() { - SCRIPTPATH="$( cd "$(dirname "")" >/dev/null 2>&1 ; pwd -P )" - cd "${SCRIPTPATH}" + cd "$(dirname "")" echo "Working Dir: " $(pwd) - source "/home/abaddon/Portable_Apps/py-venvs/flask-apps-venv/venv/bin/activate" - python ./solarfm + source "/home/abaddon/Portable_Apps/py-venvs/gtk-apps-venv/venv/bin/activate" + python -m nuitka --follow-imports --standalone --linux-onefile-icon="/home/abaddon/.config/solarfm/solarfm.png" solarfm/__main__.py } main "$@"; diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py index 6a787f4..4a5e5f8 100644 --- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py +++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller_data.py @@ -120,7 +120,7 @@ class Controller_Data: self.warning_color = self.settings.get_warning_color() self.error_color = self.settings.get_error_color() - sys.excepthook = self.custom_except_hook + # sys.excepthook = self.custom_except_hook self.window.connect("delete-event", self.tear_down) GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, self.tear_down) -- 2.27.0 From 3f5664da5b7173b284e71dc90a9011c97a929479 Mon Sep 17 00:00:00 2001 From: itdominator <1itdominator@gmail.com> Date: Wed, 20 Jul 2022 23:57:06 -0500 Subject: [PATCH 2/4] Updated READNE, added searcher plugin, cleanup --- README.md | 3 - plugins/file_properties/__init__.py | 3 + plugins/file_properties/plugin.py | 2 +- plugins/searcher/__init__.py | 3 + plugins/searcher/plugin.py | 281 +++++++++++++++++++++++++++ plugins/searcher/search_dialog.glade | 227 ++++++++++++++++++++++ plugins/template/__init__.py | 3 + plugins/template/plugin.py | 2 +- plugins/youtube_download/__init__.py | 3 + plugins/youtube_download/download.sh | 1 - plugins/youtube_download/plugin.py | 2 +- 11 files changed, 523 insertions(+), 7 deletions(-) create mode 100644 plugins/file_properties/__init__.py create mode 100644 plugins/searcher/__init__.py create mode 100644 plugins/searcher/plugin.py create mode 100644 plugins/searcher/search_dialog.glade create mode 100644 plugins/template/__init__.py create mode 100644 plugins/youtube_download/__init__.py diff --git a/README.md b/README.md index c61ddf6..f405c7f 100644 --- a/README.md +++ b/README.md @@ -11,10 +11,7 @@ sudo apt-get install python3.8 wget python3-setproctitle python3-gi ffmpegthumbn # TODO diff --git a/plugins/file_properties/__init__.py b/plugins/file_properties/__init__.py new file mode 100644 index 0000000..d36fa8c --- /dev/null +++ b/plugins/file_properties/__init__.py @@ -0,0 +1,3 @@ +""" + Pligin Module +""" diff --git a/plugins/file_properties/plugin.py b/plugins/file_properties/plugin.py index 9236370..5a4b2a4 100644 --- a/plugins/file_properties/plugin.py +++ b/plugins/file_properties/plugin.py @@ -2,7 +2,7 @@ import os, threading, subprocess, time, pwd, grp from datetime import datetime -# Gtk imports +# Lib imports import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk, GLib, Gio diff --git a/plugins/searcher/__init__.py b/plugins/searcher/__init__.py new file mode 100644 index 0000000..d36fa8c --- /dev/null +++ b/plugins/searcher/__init__.py @@ -0,0 +1,3 @@ +""" + Pligin Module +""" diff --git a/plugins/searcher/plugin.py b/plugins/searcher/plugin.py new file mode 100644 index 0000000..ea295eb --- /dev/null +++ b/plugins/searcher/plugin.py @@ -0,0 +1,281 @@ +# Python imports +import os, multiprocessing, threading, subprocess, inspect, time, json +from multiprocessing import Manager, Process + +# Lib imports +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, GLib, GObject + +# Application imports + + +# NOTE: Threads WILL NOT die with parent's destruction. +def threaded(fn): + def wrapper(*args, **kwargs): + threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=False).start() + return wrapper + +# NOTE: Threads WILL die with parent's destruction. +def daemon_threaded(fn): + def wrapper(*args, **kwargs): + threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start() + return wrapper + + + + +class Manifest: + path: str = os.path.dirname(os.path.realpath(__file__)) + name: str = "Search" + author: str = "ITDominator" + version: str = "0.0.1" + support: str = "" + requests: {} = { + 'ui_target': "context_menu", + 'pass_fm_events': "true", + 'bind_keys': [f"{name}||_show_grep_list_page:f"] + } + + + + +class FilePreviewWidget(Gtk.LinkButton): + def __init__(self, path, file): + super(FilePreviewWidget, self).__init__() + self.set_label(file) + self.set_uri(f"file://{path}") + self.show_all() + + +class GrepPreviewWidget(Gtk.Box): + def __init__(self, path, sub_keys, data): + super(GrepPreviewWidget, self).__init__() + self.set_orientation(Gtk.Orientation.VERTICAL) + self.line_color = "#e0cc64" + + + _label = '/'.join( path.split("/")[-3:] ) + title = Gtk.LinkButton.new_with_label(uri=f"file://{path}", label=_label) + + self.add(title) + for key in sub_keys: + line_num = key + text = data[key] + box = Gtk.Box() + number_label = Gtk.Label() + text_view = Gtk.Label(label=text[:-1]) + label_text = f"{line_num}" + + number_label.set_markup(label_text) + number_label.set_margin_left(15) + number_label.set_margin_right(5) + number_label.set_margin_top(5) + number_label.set_margin_bottom(5) + text_view.set_margin_top(5) + text_view.set_margin_bottom(5) + text_view.set_line_wrap(True) + + box.add(number_label) + box.add(text_view) + self.add(box) + + self.show_all() + + + +manager = Manager() +grep_result_set = manager.dict() +file_result_set = manager.list() + + +class Plugin(Manifest): + def __init__(self): + self._GLADE_FILE = f"{self.path}/search_dialog.glade" + self._builder = None + self._search_dialog = None + + self._event_system = None + self._event_sleep_time = .5 + self._event_message = None + + self._active_path = None + self._file_list = None + self._grep_list = None + self._grep_proc = None + self._list_proc = None + + + def get_ui_element(self): + self._builder = Gtk.Builder() + self._builder.add_from_file(self._GLADE_FILE) + + classes = [self] + handlers = {} + for c in classes: + methods = None + try: + methods = inspect.getmembers(c, predicate=inspect.ismethod) + handlers.update(methods) + except Exception as e: + print(repr(e)) + + self._builder.connect_signals(handlers) + + self._search_dialog = self._builder.get_object("search_dialog") + self._grep_list = self._builder.get_object("grep_list") + self._file_list = self._builder.get_object("file_list") + + GObject.signal_new("update-file-ui-signal", self._search_dialog, GObject.SIGNAL_RUN_LAST, GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,)) + self._search_dialog.connect("update-file-ui-signal", self._load_file_ui) + GObject.signal_new("update-grep-ui-signal", self._search_dialog, GObject.SIGNAL_RUN_LAST, GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,)) + self._search_dialog.connect("update-grep-ui-signal", self._load_grep_ui) + + button = Gtk.Button(label=self.name) + button.connect("button-release-event", self._show_grep_list_page) + return button + + def set_fm_event_system(self, fm_event_system): + self._event_system = fm_event_system + + def run(self): + self._module_event_observer() + + + @daemon_threaded + def _show_grep_list_page(self, widget=None, eve=None): + self._event_system.push_gui_event([self.name, "get_current_state", ()]) + self.wait_for_fm_message() + + state = self._event_message + self._event_message = None + + GLib.idle_add(self._process_queries, (state)) + + def _process_queries(self, state): + self._active_path = state.tab.get_current_directory() + response = self._search_dialog.run() + self._search_dialog.hide() + + + def _run_find_file_query(self, widget=None, eve=None): + if self._list_proc: + self._list_proc.terminate() + self._list_proc = None + time.sleep(.2) + + del file_result_set[:] + self.clear_children(self._file_list) + + query = widget.get_text() + if query: + self._list_proc = multiprocessing.Process(self._do_list_search(self._active_path, query)) + self._list_proc.start() + + def _do_list_search(self, path, query): + self._file_traverse_path(path, query) + for target, file in file_result_set: + widget = FilePreviewWidget(target, file) + self._search_dialog.emit("update-file-ui-signal", (widget)) + + def _load_file_ui(self, parent=None, widget=None): + self._file_list.add(widget) + + def _file_traverse_path(self, path, query): + try: + for file in os.listdir(path): + target = os.path.join(path, file) + if os.path.isdir(target): + self._file_traverse_path(target, query) + else: + if query.lower() in file.lower(): + file_result_set.append([target, file]) + except Exception as e: + if debug: + print("Couldn't traverse to path. Might be permissions related...") + + + def _run_grep_query(self, widget=None, eve=None): + if self._grep_proc: + self._grep_proc.terminate() + self._grep_proc = None + time.sleep(.2) + + grep_result_set.clear() + self.clear_children(self._grep_list) + + query = widget.get_text() + if query: + self._grep_proc = multiprocessing.Process(self._do_grep_search(self._active_path, query)) + self._grep_proc.start() + + def _do_grep_search(self, path, query): + self._grep_traverse_path(path, query) + + keys = grep_result_set.keys() + for key in keys: + sub_keys = grep_result_set[key].keys() + widget = GrepPreviewWidget(key, sub_keys, grep_result_set[key]) + self._search_dialog.emit("update-grep-ui-signal", (widget)) + + def _load_grep_ui(self, parent=None, widget=None): + self._grep_list.add(widget) + + def _grep_traverse_path(self, path, query): + try: + for file in os.listdir(path): + target = os.path.join(path, file) + if os.path.isdir(target): + self._grep_traverse_path(target, query) + else: + self._search_for_string(target, query) + except Exception as e: + if debug: + print("Couldn't traverse to path. Might be permissions related...") + + def _search_for_string(self, file, query): + try: + with open(file, 'r') as fp: + for i, line in enumerate(fp): + if query in line: + if f"{file}" in grep_result_set.keys(): + grep_result_set[f"{file}"][f"{i+1}"] = line + else: + grep_result_set[f"{file}"] = {} + grep_result_set[f"{file}"] = {f"{i+1}": line} + except Exception as e: + if debug: + print("Couldn't read file. Might be binary or other cause...") + + + + + def clear_children(self, widget: type) -> None: + ''' Clear children of a gtk widget. ''' + for child in widget.get_children(): + widget.remove(child) + + def wait_for_fm_message(self): + while not self._event_message: + pass + + @daemon_threaded + def _module_event_observer(self): + while True: + time.sleep(self._event_sleep_time) + event = self._event_system.read_module_event() + if event: + try: + if event[0] == self.name: + target_id, method_target, data = self._event_system.consume_module_event() + + if not method_target: + self._event_message = data + else: + method = getattr(self.__class__, f"{method_target}") + if data: + data = method(*(self, *data)) + else: + method(*(self,)) + except Exception as e: + print(repr(e)) diff --git a/plugins/searcher/search_dialog.glade b/plugins/searcher/search_dialog.glade new file mode 100644 index 0000000..47fc1bc --- /dev/null +++ b/plugins/searcher/search_dialog.glade @@ -0,0 +1,227 @@ + + + + + + False + 6 + Search... + True + center-on-parent + 720 + 620 + True + dialog + True + True + center + + + True + False + 12 + + + True + False + end + + + gtk-cancel + True + True + True + False + True + + + True + True + 0 + + + + + gtk-ok + True + True + True + False + True + + + True + True + 1 + + + + + False + False + end + 0 + + + + + True + True + False + + + True + False + vertical + + + True + True + Query... + edit-find-symbolic + False + False + Search for file... + + + + False + True + 0 + + + + + True + True + True + True + in + + + True + False + + + True + False + vertical + 5 + top + + + + + + + + + + True + True + 1 + + + + + + + True + False + File Search + + + False + + + + + True + False + vertical + + + True + True + Query... + edit-find-symbolic + False + False + Query string in file... + + + + False + True + 0 + + + + + True + True + True + True + in + + + True + False + + + True + False + vertical + 5 + top + + + + + + + + + + True + True + 1 + + + + + 1 + + + + + True + False + Grep Search + + + 1 + False + + + + + + + + + + + True + True + 1 + + + + + + cancel_button + ok_button + + + diff --git a/plugins/template/__init__.py b/plugins/template/__init__.py new file mode 100644 index 0000000..d36fa8c --- /dev/null +++ b/plugins/template/__init__.py @@ -0,0 +1,3 @@ +""" + Pligin Module +""" diff --git a/plugins/template/plugin.py b/plugins/template/plugin.py index 294aadc..bc22725 100644 --- a/plugins/template/plugin.py +++ b/plugins/template/plugin.py @@ -1,7 +1,7 @@ # Python imports import os, threading, subprocess, time -# Gtk imports +# Lib imports import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk diff --git a/plugins/youtube_download/__init__.py b/plugins/youtube_download/__init__.py new file mode 100644 index 0000000..d36fa8c --- /dev/null +++ b/plugins/youtube_download/__init__.py @@ -0,0 +1,3 @@ +""" + Pligin Module +""" diff --git a/plugins/youtube_download/download.sh b/plugins/youtube_download/download.sh index dc6df21..073b354 100755 --- a/plugins/youtube_download/download.sh +++ b/plugins/youtube_download/download.sh @@ -10,7 +10,6 @@ function main() { cd "$(dirname "")" echo "Working Dir: " $(pwd) - source "/home/abaddon/Portable_Apps/py-venvs/yt-dlp-venv/venv/bin/activate" LINK=`xclip -selection clipboard -o` yt-dlp --write-sub --embed-sub --sub-langs en -o "${1}/%(title)s.%(ext)s" "${LINK}" diff --git a/plugins/youtube_download/plugin.py b/plugins/youtube_download/plugin.py index a7a6c44..cd98a37 100644 --- a/plugins/youtube_download/plugin.py +++ b/plugins/youtube_download/plugin.py @@ -1,7 +1,7 @@ # Python imports import os, threading, subprocess, time -# Gtk imports +# Lib imports import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk -- 2.27.0 From b058dc3667b21897de8e70ab6cac7d96b4f4dffb Mon Sep 17 00:00:00 2001 From: itdominator <1itdominator@gmail.com> Date: Tue, 9 Aug 2022 20:10:25 -0500 Subject: [PATCH 3/4] Global threading decorators; Endpoint registry creation --- plugins/vod_thumbnailer/__init__.py | 3 + plugins/vod_thumbnailer/plugin.py | 173 ++++++++++++++ plugins/vod_thumbnailer/re_thumbnailer.glade | 219 ++++++++++++++++++ .../SolarFM/solarfm/__builtins__.py | 39 +++- .../solarfm-0.0.1/SolarFM/solarfm/app.py | 2 +- .../SolarFM/solarfm/core/controller.py | 22 +- .../core/mixins/exception_hook_mixin.py | 6 +- .../solarfm/core/mixins/ui/grid_mixin.py | 8 +- .../mixins/ui/widget_file_action_mixin.py | 6 +- 9 files changed, 444 insertions(+), 34 deletions(-) create mode 100644 plugins/vod_thumbnailer/__init__.py create mode 100644 plugins/vod_thumbnailer/plugin.py create mode 100644 plugins/vod_thumbnailer/re_thumbnailer.glade diff --git a/plugins/vod_thumbnailer/__init__.py b/plugins/vod_thumbnailer/__init__.py new file mode 100644 index 0000000..d36fa8c --- /dev/null +++ b/plugins/vod_thumbnailer/__init__.py @@ -0,0 +1,3 @@ +""" + Pligin Module +""" diff --git a/plugins/vod_thumbnailer/plugin.py b/plugins/vod_thumbnailer/plugin.py new file mode 100644 index 0000000..44deeb2 --- /dev/null +++ b/plugins/vod_thumbnailer/plugin.py @@ -0,0 +1,173 @@ +# Python imports +import os, threading, subprocess, time, inspect, hashlib +from datetime import datetime + +# Gtk imports +import gi +gi.require_version('Gtk', '3.0') +gi.require_version('GdkPixbuf', '2.0') +from gi.repository import Gtk, GLib, Gio, GdkPixbuf + +# Application imports + + +# NOTE: Threads WILL NOT die with parent's destruction. +def threaded(fn): + def wrapper(*args, **kwargs): + threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=False).start() + return wrapper + +# NOTE: Threads WILL die with parent's destruction. +def daemon_threaded(fn): + def wrapper(*args, **kwargs): + threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start() + return wrapper + + + + +class Manifest: + path: str = os.path.dirname(os.path.realpath(__file__)) + name: str = "VOD Thumbnailer" + author: str = "ITDominator" + version: str = "0.0.1" + support: str = "" + requests: {} = { + 'ui_target': "context_menu", + 'pass_fm_events': "true" + } + +class Plugin(Manifest): + def __init__(self): + self._GLADE_FILE = f"{self.path}/re_thumbnailer.glade" + self._builder = None + self._thumbnailer_dialog = None + self._thumbnail_preview_img = None + self._file_name = None + self._file_location = None + self._file_hash = None + self._state = None + + self._event_system = None + self._event_sleep_time = .5 + self._event_message = None + + + + def get_ui_element(self): + self._builder = Gtk.Builder() + self._builder.add_from_file(self._GLADE_FILE) + + classes = [self] + handlers = {} + for c in classes: + methods = None + try: + methods = inspect.getmembers(c, predicate=inspect.ismethod) + handlers.update(methods) + except Exception as e: + print(repr(e)) + + self._builder.connect_signals(handlers) + + self._thumbnailer_dialog = self._builder.get_object("thumbnailer_dialog") + self._file_name = self._builder.get_object("file_name") + self._file_location = self._builder.get_object("file_location") + self._thumbnail_preview_img = self._builder.get_object("thumbnail_preview_img") + self._file_hash = self._builder.get_object("file_hash") + + button = Gtk.Button(label=self.name) + button.connect("button-release-event", self._show_thumbnailer_page) + return button + + def set_fm_event_system(self, fm_event_system): + self._event_system = fm_event_system + + def run(self): + self._module_event_observer() + + + + + @threaded + def _show_thumbnailer_page(self, widget=None, eve=None): + self._event_system.push_gui_event([self.name, "get_current_state", ()]) + self.wait_for_fm_message() + + state = self._event_message + self._event_message = None + + GLib.idle_add(self._process_changes, (state)) + + def _process_changes(self, state): + self._state = None + + if len(state.selected_files) == 1: + if state.selected_files[0].lower().endswith(state.tab.fvideos): + self._state = state + self._set_ui_data() + response = self._thumbnailer_dialog.run() + if response in [Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT]: + self._thumbnailer_dialog.hide() + + + def _regenerate_thumbnail(self, widget=None, eve=None): + print("Regenerating thumbnail...") + file = self._file_name.get_text() + dir = self._file_location.get_text() + file_hash = self._file_hash.get_text() + + hash_img_pth = f"{self._state.tab.ABS_THUMBS_PTH}/{file_hash}.jpg" + try: + if os.path.isfile(hash_img_pth): + os.remove(hash_img_pth) + + img_pixbuf = self._state.tab.create_icon(dir, file) + self._thumbnail_preview_img.set_from_pixbuf(img_pixbuf) + except Exception as e: + print("Couldn't regenerate thumbnail!") + + def _use_selected_thumbnail(self, widget=None, eve=None): + print("_use_selected_thumbnail stub...") + + + def _set_ui_data(self): + uri = self._state.selected_files[0] + path = self._state.tab.get_current_directory() + parts = uri.split("/") + + file_hash = hashlib.sha256(str.encode(uri)).hexdigest() + hash_img_pth = f"{self._state.tab.ABS_THUMBS_PTH}/{file_hash}.jpg" + img_pixbuf = GdkPixbuf.Pixbuf.new_from_file(hash_img_pth) + + self._thumbnail_preview_img.set_from_pixbuf(img_pixbuf) + self._file_name.set_text(parts[ len(parts) - 1 ]) + self._file_location.set_text(path) + self._file_hash.set_text(file_hash) + + + + def wait_for_fm_message(self): + while not self._event_message: + pass + + @daemon_threaded + def _module_event_observer(self): + while True: + time.sleep(self._event_sleep_time) + event = self._event_system.read_module_event() + if event: + try: + if event[0] == self.name: + target_id, method_target, data = self._event_system.consume_module_event() + + if not method_target: + self._event_message = data + else: + method = getattr(self.__class__, f"{method_target}") + if data: + data = method(*(self, *data)) + else: + method(*(self,)) + except Exception as e: + print(repr(e)) diff --git a/plugins/vod_thumbnailer/re_thumbnailer.glade b/plugins/vod_thumbnailer/re_thumbnailer.glade new file mode 100644 index 0000000..1198dc4 --- /dev/null +++ b/plugins/vod_thumbnailer/re_thumbnailer.glade @@ -0,0 +1,219 @@ + + + + + + False + 6 + VOD Thumbnailer + True + center-on-parent + 420 + True + dialog + True + True + center + + + True + False + 12 + + + True + False + end + + + gtk-cancel + True + True + True + False + True + + + True + True + 0 + + + + + False + False + end + 0 + + + + + True + False + vertical + + + True + False + gtk-missing-image + 6 + + + True + True + 0 + + + + + + + + True + False + 4 + 4 + 2 + 12 + 6 + + + True + False + <b>File _Name:</b> + True + True + file_name + 0 + + + GTK_FILL + + + + + + True + True + False + + + 1 + 2 + + + + + + True + False + <b>_Location:</b> + True + True + file_location + 0 + + + 1 + 2 + GTK_FILL + + + + + + True + True + False + + + 1 + 2 + 1 + 2 + GTK_FILL + + + + + + Regenerate + True + True + True + + + + 1 + 2 + 3 + 4 + + + + + Use Selected + True + True + True + + + + 3 + 4 + + + + + True + False + <b>_Thumbnail Hash:</b> + True + True + file_location + 0 + + + 2 + 3 + GTK_FILL + + + + + + True + True + False + + + 1 + 2 + 2 + 3 + GTK_FILL + + + + + + False + True + 2 + + + + + True + True + 1 + + + + + + cancel_button + + + diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/__builtins__.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/__builtins__.py index ae1c305..15a4f9d 100644 --- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/__builtins__.py +++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/__builtins__.py @@ -1,5 +1,5 @@ # Python imports -import builtins +import builtins, threading # Lib imports @@ -7,6 +7,21 @@ import builtins from utils.ipc_server import IPCServer +# NOTE: Threads will not die with parent's destruction +def threaded_wrapper(fn): + def wrapper(*args, **kwargs): + threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=False).start() + return wrapper + +# NOTE: Insure threads die with parent's destruction +def daemon_threaded_wrapper(fn): + def wrapper(*args, **kwargs): + threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start() + return wrapper + + + + class EventSystem(IPCServer): @@ -65,10 +80,32 @@ class EventSystem(IPCServer): +class EndpointRegistry(): + def __init__(self): + self._endpoints = {} + + def register(self, rule, **options): + def decorator(f): + _endpoint = options.pop("endpoint", None) + self._endpoints[rule] = f + return f + + return decorator + + def get_endpoints(self): + return self._endpoints + + + + # NOTE: Just reminding myself we can add to builtins two different ways... # __builtins__.update({"event_system": Builtins()}) builtins.app_name = "SolarFM" builtins.event_system = EventSystem() +builtins.endpoint_registry = EndpointRegistry() +builtins.threaded = threaded_wrapper +builtins.daemon_threaded = daemon_threaded_wrapper builtins.event_sleep_time = 0.05 builtins.trace_debug = False builtins.debug = False +builtins.app_settings = None diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/app.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/app.py index bc414b5..e7b0d5f 100644 --- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/app.py +++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/app.py @@ -4,9 +4,9 @@ import os, inspect, time # Lib imports # Application imports +from __builtins__ import EventSystem from utils.settings import Settings from core.controller import Controller -from __builtins__ import EventSystem diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py index 3b7ba50..6ddb4c9 100644 --- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py +++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py @@ -1,5 +1,5 @@ # Python imports -import os, gc, threading, time +import os, gc, time # Lib imports import gi @@ -14,19 +14,6 @@ from .signals.keyboard_signals_mixin import KeyboardSignalsMixin from .controller_data import Controller_Data -# NOTE: Threads will not die with parent's destruction -def threaded(fn): - def wrapper(*args, **kwargs): - threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=False).start() - return wrapper - -# NOTE: Insure threads die with parent's destruction -def daemon_threaded(fn): - def wrapper(*args, **kwargs): - threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start() - return wrapper - - class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMixin, Controller_Data): @@ -177,23 +164,28 @@ class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMi - + @endpoint_registry.register(rule="go_home") def go_home(self, widget=None, eve=None): self.builder.get_object("go_home").released() + @endpoint_registry.register(rule="refresh_tab") def refresh_tab(self, widget=None, eve=None): self.builder.get_object("refresh_tab").released() + @endpoint_registry.register(rule="go_up") def go_up(self, widget=None, eve=None): self.builder.get_object("go_up").released() + @endpoint_registry.register(rule="grab_focus_path_entry") def grab_focus_path_entry(self, widget=None, eve=None): self.builder.get_object("path_entry").grab_focus() + @endpoint_registry.register(rule="tggl_top_main_menubar") def tggl_top_main_menubar(self, widget=None, eve=None): top_main_menubar = self.builder.get_object("top_main_menubar") top_main_menubar.hide() if top_main_menubar.is_visible() else top_main_menubar.show() + @endpoint_registry.register(rule="open_terminal") def open_terminal(self, widget=None, eve=None): wid, tid = self.fm_controller.get_active_wid_and_tid() tab = self.get_fm_window(wid).get_tab_by_id(tid) diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/exception_hook_mixin.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/exception_hook_mixin.py index 81a0b95..660b9b6 100644 --- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/exception_hook_mixin.py +++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/exception_hook_mixin.py @@ -1,5 +1,5 @@ # Python imports -import traceback, threading, time +import traceback, time # Lib imports import gi @@ -9,10 +9,6 @@ from gi.repository import Gtk, GLib # Application imports -def threaded(fn): - def wrapper(*args, **kwargs): - threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start() - return wrapper class ExceptionHookMixin: diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/grid_mixin.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/grid_mixin.py index 01ff051..2d3afeb 100644 --- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/grid_mixin.py +++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/grid_mixin.py @@ -1,5 +1,5 @@ # Python imports -import os, threading, subprocess, time +import os # Lib imports import gi @@ -11,12 +11,6 @@ from gi.repository import Gtk, Gdk, GLib, Gio, GdkPixbuf # Application imports -def threaded(fn): - def wrapper(*args, **kwargs): - threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start() - return wrapper - - # NOTE: Consider trying to use Gtk.TreeView with css that turns it into a grid... diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/widget_file_action_mixin.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/widget_file_action_mixin.py index 9fc5a83..71a3cc3 100644 --- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/widget_file_action_mixin.py +++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/mixins/ui/widget_file_action_mixin.py @@ -1,5 +1,5 @@ # Python imports -import os, time, threading, shlex +import os, time, shlex # Lib imports import gi @@ -9,10 +9,6 @@ from gi.repository import Gtk, GObject, GLib, Gio # Application imports -def threaded(fn): - def wrapper(*args, **kwargs): - threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start() - return wrapper class WidgetFileActionMixin: -- 2.27.0 From bff54995fd116e78bf61b8b361a6e1fb756c35de Mon Sep 17 00:00:00 2001 From: itdominator <1itdominator@gmail.com> Date: Fri, 12 Aug 2022 22:54:16 -0500 Subject: [PATCH 4/4] Reworked plugin manifest; decoupled event system and ipc --- plugins/file_properties/__main__.py | 3 + plugins/file_properties/manifest.json | 12 +++ plugins/file_properties/plugin.py | 16 +--- plugins/searcher/__main__.py | 3 + plugins/searcher/manifest.json | 13 ++++ plugins/searcher/plugin.py | 20 +---- plugins/template/__main__.py | 3 + plugins/template/manifest.json | 13 ++++ plugins/template/plugin.py | 18 +---- plugins/vod_thumbnailer/__main__.py | 3 + plugins/vod_thumbnailer/manifest.json | 12 +++ plugins/vod_thumbnailer/plugin.py | 16 +--- plugins/youtube_download/__main__.py | 3 + plugins/youtube_download/manifest.json | 12 +++ plugins/youtube_download/plugin.py | 17 +---- .../SolarFM/solarfm/__builtins__.py | 67 ++-------------- .../solarfm-0.0.1/SolarFM/solarfm/app.py | 16 ++-- .../SolarFM/solarfm/core/controller.py | 1 - .../SolarFM/solarfm/plugins/manifest.py | 76 +++++++++++++++++++ .../SolarFM/solarfm/plugins/plugins.py | 69 ++--------------- .../SolarFM/solarfm/utils/event_system.py | 59 ++++++++++++++ .../SolarFM/solarfm/utils/ipc_server.py | 8 +- 22 files changed, 253 insertions(+), 207 deletions(-) create mode 100644 plugins/file_properties/__main__.py create mode 100644 plugins/file_properties/manifest.json create mode 100644 plugins/searcher/__main__.py create mode 100644 plugins/searcher/manifest.json create mode 100644 plugins/template/__main__.py create mode 100644 plugins/template/manifest.json create mode 100644 plugins/vod_thumbnailer/__main__.py create mode 100644 plugins/vod_thumbnailer/manifest.json create mode 100644 plugins/youtube_download/__main__.py create mode 100644 plugins/youtube_download/manifest.json create mode 100644 src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/manifest.py create mode 100644 src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/event_system.py diff --git a/plugins/file_properties/__main__.py b/plugins/file_properties/__main__.py new file mode 100644 index 0000000..a576329 --- /dev/null +++ b/plugins/file_properties/__main__.py @@ -0,0 +1,3 @@ +""" + Pligin Package +""" diff --git a/plugins/file_properties/manifest.json b/plugins/file_properties/manifest.json new file mode 100644 index 0000000..7ba667d --- /dev/null +++ b/plugins/file_properties/manifest.json @@ -0,0 +1,12 @@ +{ + "manifest": { + "name": "Properties", + "author": "ITDominator", + "version": "0.0.1", + "support": "", + "requests": { + "ui_target": "context_menu", + "pass_fm_events": "true" + } + } +} diff --git a/plugins/file_properties/plugin.py b/plugins/file_properties/plugin.py index 5a4b2a4..a7f6d8e 100644 --- a/plugins/file_properties/plugin.py +++ b/plugins/file_properties/plugin.py @@ -25,17 +25,6 @@ def daemon_threaded(fn): -class Manifest: - path: str = os.path.dirname(os.path.realpath(__file__)) - name: str = "Properties" - author: str = "ITDominator" - version: str = "0.0.1" - support: str = "" - requests: {} = { - 'ui_target': "context_menu", - 'pass_fm_events': "true" - } - class Properties: file_uri: str = None file_name: str = None @@ -50,8 +39,11 @@ class Properties: chmod_stat: str = None -class Plugin(Manifest): +class Plugin: def __init__(self): + self.path = os.path.dirname(os.path.realpath(__file__)) + self.name = "Properties" # NOTE: Need to remove after establishing private bidirectional 1-1 message bus + # where self.name should not be needed for message comms self._GLADE_FILE = f"{self.path}/file_properties.glade" self._builder = None self._properties_dialog = None diff --git a/plugins/searcher/__main__.py b/plugins/searcher/__main__.py new file mode 100644 index 0000000..a576329 --- /dev/null +++ b/plugins/searcher/__main__.py @@ -0,0 +1,3 @@ +""" + Pligin Package +""" diff --git a/plugins/searcher/manifest.json b/plugins/searcher/manifest.json new file mode 100644 index 0000000..845a31f --- /dev/null +++ b/plugins/searcher/manifest.json @@ -0,0 +1,13 @@ +{ + "manifest": { + "name": "Search", + "author": "ITDominator", + "version": "0.0.1", + "support": "", + "requests": { + "ui_target": "context_menu", + "pass_fm_events": "true", + "bind_keys": ["Search||_show_grep_list_page:f"] + } + } +} diff --git a/plugins/searcher/plugin.py b/plugins/searcher/plugin.py index ea295eb..4721b32 100644 --- a/plugins/searcher/plugin.py +++ b/plugins/searcher/plugin.py @@ -25,21 +25,6 @@ def daemon_threaded(fn): -class Manifest: - path: str = os.path.dirname(os.path.realpath(__file__)) - name: str = "Search" - author: str = "ITDominator" - version: str = "0.0.1" - support: str = "" - requests: {} = { - 'ui_target': "context_menu", - 'pass_fm_events': "true", - 'bind_keys': [f"{name}||_show_grep_list_page:f"] - } - - - - class FilePreviewWidget(Gtk.LinkButton): def __init__(self, path, file): super(FilePreviewWidget, self).__init__() @@ -89,8 +74,11 @@ grep_result_set = manager.dict() file_result_set = manager.list() -class Plugin(Manifest): +class Plugin: def __init__(self): + self.path = os.path.dirname(os.path.realpath(__file__)) + self.name = "Search" # NOTE: Need to remove after establishing private bidirectional 1-1 message bus + # where self.name should not be needed for message comms self._GLADE_FILE = f"{self.path}/search_dialog.glade" self._builder = None self._search_dialog = None diff --git a/plugins/template/__main__.py b/plugins/template/__main__.py new file mode 100644 index 0000000..a576329 --- /dev/null +++ b/plugins/template/__main__.py @@ -0,0 +1,3 @@ +""" + Pligin Package +""" diff --git a/plugins/template/manifest.json b/plugins/template/manifest.json new file mode 100644 index 0000000..4dcbf47 --- /dev/null +++ b/plugins/template/manifest.json @@ -0,0 +1,13 @@ +{ + "manifest": { + "name": "Example Plugin", + "author": "John Doe", + "version": "0.0.1", + "support": "", + "requests": { + "ui_target": "plugin_control_list", + "pass_fm_events": "true", + "bind_keys": ["Example Plugin||send_message:f"] + } + } +} diff --git a/plugins/template/plugin.py b/plugins/template/plugin.py index bc22725..21a5d5b 100644 --- a/plugins/template/plugin.py +++ b/plugins/template/plugin.py @@ -24,21 +24,10 @@ def daemon_threaded(fn): -class Manifest: - path: str = os.path.dirname(os.path.realpath(__file__)) - name: str = "Example Plugin" - author: str = "John Doe" - version: str = "0.0.1" - support: str = "" - requests: {} = { - 'ui_target': "plugin_control_list", - 'pass_fm_events': "true", - 'bind_keys': [f"{name}||send_message:f"] - } - - -class Plugin(Manifest): +class Plugin: def __init__(self): + self.name = "Example Plugin" # NOTE: Need to remove after establishing private bidirectional 1-1 message bus + # where self.name should not be needed for message comms self._event_system = None self._event_sleep_time = .5 self._event_message = None @@ -58,7 +47,6 @@ class Plugin(Manifest): def send_message(self, widget=None, eve=None): message = "Hello, World!" - print("here") self._event_system.push_gui_event([self.name, "display_message", ("warning", message, None)]) diff --git a/plugins/vod_thumbnailer/__main__.py b/plugins/vod_thumbnailer/__main__.py new file mode 100644 index 0000000..a576329 --- /dev/null +++ b/plugins/vod_thumbnailer/__main__.py @@ -0,0 +1,3 @@ +""" + Pligin Package +""" diff --git a/plugins/vod_thumbnailer/manifest.json b/plugins/vod_thumbnailer/manifest.json new file mode 100644 index 0000000..154bd40 --- /dev/null +++ b/plugins/vod_thumbnailer/manifest.json @@ -0,0 +1,12 @@ +{ + "manifest": { + "name": "VOD Thumbnailer", + "author": "ITDominator", + "version": "0.0.1", + "support": "", + "requests": { + "ui_target": "context_menu", + "pass_fm_events": "true" + } + } +} diff --git a/plugins/vod_thumbnailer/plugin.py b/plugins/vod_thumbnailer/plugin.py index 44deeb2..1fbc4ac 100644 --- a/plugins/vod_thumbnailer/plugin.py +++ b/plugins/vod_thumbnailer/plugin.py @@ -26,19 +26,11 @@ def daemon_threaded(fn): -class Manifest: - path: str = os.path.dirname(os.path.realpath(__file__)) - name: str = "VOD Thumbnailer" - author: str = "ITDominator" - version: str = "0.0.1" - support: str = "" - requests: {} = { - 'ui_target': "context_menu", - 'pass_fm_events': "true" - } - -class Plugin(Manifest): +class Plugin: def __init__(self): + self.path = os.path.dirname(os.path.realpath(__file__)) + self.name = "VOD Thumbnailer" # NOTE: Need to remove after establishing private bidirectional 1-1 message bus + # where self.name should not be needed for message comms self._GLADE_FILE = f"{self.path}/re_thumbnailer.glade" self._builder = None self._thumbnailer_dialog = None diff --git a/plugins/youtube_download/__main__.py b/plugins/youtube_download/__main__.py new file mode 100644 index 0000000..a576329 --- /dev/null +++ b/plugins/youtube_download/__main__.py @@ -0,0 +1,3 @@ +""" + Pligin Package +""" diff --git a/plugins/youtube_download/manifest.json b/plugins/youtube_download/manifest.json new file mode 100644 index 0000000..554ce1e --- /dev/null +++ b/plugins/youtube_download/manifest.json @@ -0,0 +1,12 @@ +{ + "manifest": { + "name": "Youtube Download", + "author": "ITDominator", + "version": "0.0.1", + "support": "", + "requests": { + "ui_target": "plugin_control_list", + "pass_fm_events": "true" + } + } +} diff --git a/plugins/youtube_download/plugin.py b/plugins/youtube_download/plugin.py index cd98a37..122cc99 100644 --- a/plugins/youtube_download/plugin.py +++ b/plugins/youtube_download/plugin.py @@ -24,21 +24,10 @@ def daemon_threaded(fn): -class Manifest: - path: str = os.path.dirname(os.path.realpath(__file__)) - name: str = "Youtube Download" - author: str = "ITDominator" - version: str = "0.0.1" - support: str = "" - requests: {} = { - 'ui_target': "plugin_control_list", - 'pass_fm_events': "true" - - } - - -class Plugin(Manifest): +class Plugin: def __init__(self): + self.name = "Youtube Download" # NOTE: Need to remove after establishing private bidirectional 1-1 message bus + # where self.name should not be needed for message comms self._event_system = None self._event_sleep_time = .5 self._event_message = None diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/__builtins__.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/__builtins__.py index 15a4f9d..d9a77d8 100644 --- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/__builtins__.py +++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/__builtins__.py @@ -4,16 +4,18 @@ import builtins, threading # Lib imports # Application imports -from utils.ipc_server import IPCServer +from utils.event_system import EventSystem -# NOTE: Threads will not die with parent's destruction + + +# NOTE: Threads WILL NOT die with parent's destruction. def threaded_wrapper(fn): def wrapper(*args, **kwargs): threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=False).start() return wrapper -# NOTE: Insure threads die with parent's destruction +# NOTE: Threads WILL die with parent's destruction. def daemon_threaded_wrapper(fn): def wrapper(*args, **kwargs): threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start() @@ -22,71 +24,12 @@ def daemon_threaded_wrapper(fn): - - -class EventSystem(IPCServer): - """ Inheret IPCServerMixin. Create an pub/sub systems. """ - - def __init__(self): - super(EventSystem, self).__init__() - - # NOTE: The format used is list of ['who', target, (data,)] Where: - # who is the sender or target ID and is used for context and control flow, - # method_target is the method to call, - # data is the method parameters OR message data to give - # Where data may be any kind of data - self._gui_events = [] - self._module_events = [] - - - - # Makeshift "events" system FIFO - def _pop_gui_event(self) -> None: - if len(self._gui_events) > 0: - return self._gui_events.pop(0) - return None - - def _pop_module_event(self) -> None: - if len(self._module_events) > 0: - return self._module_events.pop(0) - return None - - - def push_gui_event(self, event: list) -> None: - if len(event) == 3: - self._gui_events.append(event) - return None - - raise Exception("Invald event format! Please do: ['sender_id': str, method_target: method, (data,): any]") - - def push_module_event(self, event: list) -> None: - if len(event) == 3: - self._module_events.append(event) - return None - - raise Exception("Invald event format! Please do: ['target_id': str, method_target: method, (data,): any]") - - def read_gui_event(self) -> list: - return self._gui_events[0] if self._gui_events else None - - def read_module_event(self) -> list: - return self._module_events[0] if self._module_events else None - - def consume_gui_event(self) -> list: - return self._pop_gui_event() - - def consume_module_event(self) -> list: - return self._pop_module_event() - - - class EndpointRegistry(): def __init__(self): self._endpoints = {} def register(self, rule, **options): def decorator(f): - _endpoint = options.pop("endpoint", None) self._endpoints[rule] = f return f diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/app.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/app.py index e7b0d5f..a0e36e9 100644 --- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/app.py +++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/app.py @@ -4,31 +4,33 @@ import os, inspect, time # Lib imports # Application imports -from __builtins__ import EventSystem +from __builtins__ import * +from utils.ipc_server import IPCServer from utils.settings import Settings from core.controller import Controller - -class Application(EventSystem): +class Application(IPCServer): """ Create Settings and Controller classes. Bind signal to Builder. Inherit from Builtins to bind global methods and classes. """ def __init__(self, args, unknownargs): + super(Application, self).__init__() + if not trace_debug: - event_system.create_ipc_listener() + self.create_ipc_listener() time.sleep(0.05) - if not event_system.is_ipc_alive: + if not self.is_ipc_alive: if unknownargs: for arg in unknownargs: if os.path.isdir(arg): message = f"FILE|{arg}" - event_system.send_ipc_message(message) + self.send_ipc_message(message) if args.new_tab and os.path.isdir(args.new_tab): message = f"FILE|{args.new_tab}" - event_system.send_ipc_message(message) + self.send_ipc_message(message) raise Exception("IPC Server Exists: Will send path(s) to it and close...\nNote: If no fm exists, remove /tmp/solarfm-ipc.sock") diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py index 6ddb4c9..d476cbf 100644 --- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py +++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/core/controller.py @@ -43,7 +43,6 @@ class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMi def tear_down(self, widget=None, eve=None): - event_system.send_ipc_message("close server") self.fm_controller.save_state() time.sleep(event_sleep_time) Gtk.main_quit() diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/manifest.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/manifest.py new file mode 100644 index 0000000..6aa3f0a --- /dev/null +++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/manifest.py @@ -0,0 +1,76 @@ +# Python imports +import os, json +from os.path import join + +# Lib imports + +# Application imports + + + + +class Plugin: + path: str = None + name: str = None + author: str = None + version: str = None + support: str = None + requests:{} = None + reference: type = None + + +class ManifestProcessor: + def __init__(self, path, builder): + manifest = join(path, "manifest.json") + if not os.path.exists(manifest): + raise Exception("Invalid Plugin Structure: Plugin doesn't have 'manifest.json'. Aboarting load...") + + self._path = path + self._builder = builder + with open(manifest) as f: + data = json.load(f) + self._manifest = data["manifest"] + self._plugin = self.collect_info() + + def collect_info(self) -> Plugin: + plugin = Plugin() + plugin.path = self._path + plugin.name = self._manifest["name"] + plugin.author = self._manifest["author"] + plugin.version = self._manifest["version"] + plugin.support = self._manifest["support"] + plugin.requests = self._manifest["requests"] + + return plugin + + def get_loading_data(self): + loading_data = {} + requests = self._plugin.requests + keys = requests.keys() + + if "ui_target" in keys: + if requests["ui_target"] in [ + "none", "other", "main_Window", "main_menu_bar", "path_menu_bar", "plugin_control_list", + "context_menu", "window_1", "window_2", "window_3", "window_4" + ]: + if requests["ui_target"] == "other": + if "ui_target_id" in keys: + loading_data["ui_target"] = self._builder.get_object(requests["ui_target_id"]) + if loading_data["ui_target"] == None: + raise Exception('Invalid "ui_target_id" given in requests. Must have one if setting "ui_target" to "other"...') + else: + raise Exception('Invalid "ui_target_id" given in requests. Must have one if setting "ui_target" to "other"...') + else: + loading_data["ui_target"] = self._builder.get_object(requests["ui_target"]) + else: + raise Exception('Unknown "ui_target" given in requests.') + + if "pass_fm_events" in keys: + if requests["pass_fm_events"] in ["true"]: + loading_data["pass_fm_events"] = True + + if "bind_keys" in keys: + if isinstance(requests["bind_keys"], list): + loading_data["bind_keys"] = requests["bind_keys"] + + return self._plugin, loading_data diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugins.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugins.py index 2d547b4..b14a1a9 100644 --- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugins.py +++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/plugins/plugins.py @@ -8,17 +8,9 @@ gi.require_version('Gtk', '3.0') from gi.repository import Gtk, Gio # Application imports +from .manifest import Plugin, ManifestProcessor -class Plugin: - path: str = None - name: str = None - author: str = None - version: str = None - support: str = None - requests:{} = None - reference: type = None - class Plugins: @@ -28,7 +20,7 @@ class Plugins: self._settings = settings self._builder = self._settings.get_builder() self._plugins_path = self._settings.get_plugins_path() - self._keybindings = self._settings.get_keybindings() + self._keybindings = self._settings.get_keybindings() self._plugins_dir_watcher = None self._plugin_collection = [] @@ -55,20 +47,18 @@ class Plugins: 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") + target = join(path, "plugin.py") + manifest = ManifestProcessor(path, self._builder) + if not os.path.exists(target): raise Exception("Invalid Plugin Structure: Plugin doesn't have 'plugin.py'. Aboarting load...") - module = self.load_plugin_module(path, folder, target) - plugin = self.collect_info(module, path) - loading_data = self.parse_requests(plugin) - + plugin, loading_data = manifest.get_loading_data() + module = self.load_plugin_module(path, folder, target) self.execute_plugin(module, plugin, loading_data) except Exception as e: print(f"Malformed Plugin: Not loading -->: '{folder}' !") - traceback.print_exc() - # if debug: - # traceback.print_exc() + traceback.print_exc() os.chdir(parent_path) @@ -82,49 +72,6 @@ class Plugins: return module - def collect_info(self, module, path) -> Plugin: - plugin = Plugin() - plugin.path = module.Manifest.path - plugin.name = module.Manifest.name - plugin.author = module.Manifest.author - plugin.version = module.Manifest.version - plugin.support = module.Manifest.support - plugin.requests = module.Manifest.requests - - return plugin - - def parse_requests(self, plugin): - loading_data = {} - requests = plugin.requests - keys = requests.keys() - - if "ui_target" in keys: - if requests["ui_target"] in [ - "none", "other", "main_Window", "main_menu_bar", "path_menu_bar", "plugin_control_list", - "context_menu", "window_1", "window_2", "window_3", "window_4" - ]: - if requests["ui_target"] == "other": - if "ui_target_id" in keys: - loading_data["ui_target"] = self._builder.get_object(requests["ui_target_id"]) - if loading_data["ui_target"] == None: - raise Exception('Invalid "ui_target_id" given in requests. Must have one if setting "ui_target" to "other"...') - else: - raise Exception('Invalid "ui_target_id" given in requests. Must have one if setting "ui_target" to "other"...') - else: - loading_data["ui_target"] = self._builder.get_object(requests["ui_target"]) - else: - raise Exception('Unknown "ui_target" given in requests.') - - - if "pass_fm_events" in keys: - if requests["pass_fm_events"] in ["true"]: - loading_data["pass_fm_events"] = True - - if "bind_keys" in keys: - if isinstance(requests["bind_keys"], list): - loading_data["bind_keys"] = requests["bind_keys"] - - return loading_data def execute_plugin(self, module: type, plugin: Plugin, loading_data: []): plugin.reference = module.Plugin() diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/event_system.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/event_system.py new file mode 100644 index 0000000..1dcd7fa --- /dev/null +++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/event_system.py @@ -0,0 +1,59 @@ +# Python imports + +# Lib imports + +# Application imports + + + + +class EventSystem: + """ Inheret IPCServerMixin. Create an pub/sub systems. """ + + def __init__(self): + # NOTE: The format used is list of ['who', target, (data,)] Where: + # who is the sender or target ID and is used for context and control flow, + # method_target is the method to call, + # data is the method parameters OR message data to give + # Where data may be any kind of data + self._gui_events = [] + self._module_events = [] + + + # Makeshift "events" system FIFO + def _pop_gui_event(self) -> None: + if len(self._gui_events) > 0: + return self._gui_events.pop(0) + return None + + def _pop_module_event(self) -> None: + if len(self._module_events) > 0: + return self._module_events.pop(0) + return None + + + def push_gui_event(self, event: list) -> None: + if len(event) == 3: + self._gui_events.append(event) + return None + + raise Exception("Invald event format! Please do: ['sender_id': str, method_target: method, (data,): any]") + + def push_module_event(self, event: list) -> None: + if len(event) == 3: + self._module_events.append(event) + return None + + raise Exception("Invald event format! Please do: ['target_id': str, method_target: method, (data,): any]") + + def read_gui_event(self) -> list: + return self._gui_events[0] if self._gui_events else None + + def read_module_event(self) -> list: + return self._module_events[0] if self._module_events else None + + def consume_gui_event(self) -> list: + return self._pop_gui_event() + + def consume_module_event(self) -> list: + return self._pop_module_event() diff --git a/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/ipc_server.py b/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/ipc_server.py index 5ad037d..3b88c16 100644 --- a/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/ipc_server.py +++ b/src/versions/solarfm-0.0.1/SolarFM/solarfm/utils/ipc_server.py @@ -7,12 +7,6 @@ from multiprocessing.connection import Listener, Client # Application imports -def threaded(fn): - def wrapper(*args, **kwargs): - threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start() - return wrapper - - class IPCServer: @@ -36,7 +30,7 @@ class IPCServer: self._ipc_authkey = None - @threaded + @daemon_threaded def create_ipc_listener(self) -> None: if self._conn_type == "socket": if os.path.exists(self._ipc_address): -- 2.27.0