Merge pull request 'develop' (#4) from develop into master
Reviewed-on: #4
This commit is contained in:
		@@ -11,10 +11,7 @@ sudo apt-get install python3.8 wget python3-setproctitle python3-gi ffmpegthumbn
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# TODO
 | 
					# TODO
 | 
				
			||||||
<ul>
 | 
					<ul>
 | 
				
			||||||
<li>Add simpleish plugin system to run bash/python scripts.</li>
 | 
					 | 
				
			||||||
<li>Add simpleish preview plugin for various file types.</li>
 | 
					<li>Add simpleish preview plugin for various file types.</li>
 | 
				
			||||||
<li>Add simpleish file chmod, chown, stats, etc plugin for file management.</li>
 | 
					 | 
				
			||||||
<li>Add simpleish search plugin to do recursive search and show.</li>
 | 
					 | 
				
			||||||
<li>Add simpleish bulk-renamer.</li>
 | 
					<li>Add simpleish bulk-renamer.</li>
 | 
				
			||||||
<li>Add a basic favorites manager plugin.</li>
 | 
					<li>Add a basic favorites manager plugin.</li>
 | 
				
			||||||
</ul>
 | 
					</ul>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										3
									
								
								plugins/file_properties/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								plugins/file_properties/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					    Pligin Module
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
							
								
								
									
										3
									
								
								plugins/file_properties/__main__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								plugins/file_properties/__main__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					    Pligin Package
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
							
								
								
									
										12
									
								
								plugins/file_properties/manifest.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								plugins/file_properties/manifest.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					    "manifest": {
 | 
				
			||||||
 | 
					        "name": "Properties",
 | 
				
			||||||
 | 
					        "author": "ITDominator",
 | 
				
			||||||
 | 
					        "version": "0.0.1",
 | 
				
			||||||
 | 
					        "support": "",
 | 
				
			||||||
 | 
					        "requests": {
 | 
				
			||||||
 | 
					            "ui_target": "context_menu",
 | 
				
			||||||
 | 
					            "pass_fm_events": "true"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
import os, threading, subprocess, time, pwd, grp
 | 
					import os, threading, subprocess, time, pwd, grp
 | 
				
			||||||
from datetime import datetime
 | 
					from datetime import datetime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Gtk imports
 | 
					# Lib imports
 | 
				
			||||||
import gi
 | 
					import gi
 | 
				
			||||||
gi.require_version('Gtk', '3.0')
 | 
					gi.require_version('Gtk', '3.0')
 | 
				
			||||||
from gi.repository import Gtk, GLib, Gio
 | 
					from gi.repository import Gtk, GLib, Gio
 | 
				
			||||||
@@ -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:
 | 
					class Properties:
 | 
				
			||||||
    file_uri: str      = None
 | 
					    file_uri: str      = None
 | 
				
			||||||
    file_name: str     = None
 | 
					    file_name: str     = None
 | 
				
			||||||
@@ -50,8 +39,11 @@ class Properties:
 | 
				
			|||||||
    chmod_stat: str    = None
 | 
					    chmod_stat: str    = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Plugin(Manifest):
 | 
					class Plugin:
 | 
				
			||||||
    def __init__(self):
 | 
					    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._GLADE_FILE        = f"{self.path}/file_properties.glade"
 | 
				
			||||||
        self._builder           = None
 | 
					        self._builder           = None
 | 
				
			||||||
        self._properties_dialog = None
 | 
					        self._properties_dialog = None
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										3
									
								
								plugins/searcher/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								plugins/searcher/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					    Pligin Module
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
							
								
								
									
										3
									
								
								plugins/searcher/__main__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								plugins/searcher/__main__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					    Pligin Package
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
							
								
								
									
										13
									
								
								plugins/searcher/manifest.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								plugins/searcher/manifest.json
									
									
									
									
									
										Normal file
									
								
							@@ -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:<Control>f"]
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										269
									
								
								plugins/searcher/plugin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										269
									
								
								plugins/searcher/plugin.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,269 @@
 | 
				
			|||||||
 | 
					# 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 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"<span foreground='{self.line_color}'>{line_num}</span>"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            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:
 | 
				
			||||||
 | 
					    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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        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))
 | 
				
			||||||
							
								
								
									
										227
									
								
								plugins/searcher/search_dialog.glade
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										227
									
								
								plugins/searcher/search_dialog.glade
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,227 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<!-- Generated with glade 3.38.2 -->
 | 
				
			||||||
 | 
					<interface>
 | 
				
			||||||
 | 
					  <requires lib="gtk+" version="3.16"/>
 | 
				
			||||||
 | 
					  <object class="GtkDialog" id="search_dialog">
 | 
				
			||||||
 | 
					    <property name="can-focus">False</property>
 | 
				
			||||||
 | 
					    <property name="border-width">6</property>
 | 
				
			||||||
 | 
					    <property name="title" translatable="yes">Search...</property>
 | 
				
			||||||
 | 
					    <property name="modal">True</property>
 | 
				
			||||||
 | 
					    <property name="window-position">center-on-parent</property>
 | 
				
			||||||
 | 
					    <property name="default-width">720</property>
 | 
				
			||||||
 | 
					    <property name="default-height">620</property>
 | 
				
			||||||
 | 
					    <property name="destroy-with-parent">True</property>
 | 
				
			||||||
 | 
					    <property name="type-hint">dialog</property>
 | 
				
			||||||
 | 
					    <property name="skip-taskbar-hint">True</property>
 | 
				
			||||||
 | 
					    <property name="skip-pager-hint">True</property>
 | 
				
			||||||
 | 
					    <property name="gravity">center</property>
 | 
				
			||||||
 | 
					    <child internal-child="vbox">
 | 
				
			||||||
 | 
					      <object class="GtkBox" id="dialog_vbox">
 | 
				
			||||||
 | 
					        <property name="visible">True</property>
 | 
				
			||||||
 | 
					        <property name="can-focus">False</property>
 | 
				
			||||||
 | 
					        <property name="spacing">12</property>
 | 
				
			||||||
 | 
					        <child internal-child="action_area">
 | 
				
			||||||
 | 
					          <object class="GtkButtonBox" id="dialog_action_area">
 | 
				
			||||||
 | 
					            <property name="visible">True</property>
 | 
				
			||||||
 | 
					            <property name="can-focus">False</property>
 | 
				
			||||||
 | 
					            <property name="layout-style">end</property>
 | 
				
			||||||
 | 
					            <child>
 | 
				
			||||||
 | 
					              <object class="GtkButton" id="cancel_button">
 | 
				
			||||||
 | 
					                <property name="label">gtk-cancel</property>
 | 
				
			||||||
 | 
					                <property name="visible">True</property>
 | 
				
			||||||
 | 
					                <property name="can-focus">True</property>
 | 
				
			||||||
 | 
					                <property name="can-default">True</property>
 | 
				
			||||||
 | 
					                <property name="receives-default">False</property>
 | 
				
			||||||
 | 
					                <property name="use-stock">True</property>
 | 
				
			||||||
 | 
					              </object>
 | 
				
			||||||
 | 
					              <packing>
 | 
				
			||||||
 | 
					                <property name="expand">True</property>
 | 
				
			||||||
 | 
					                <property name="fill">True</property>
 | 
				
			||||||
 | 
					                <property name="position">0</property>
 | 
				
			||||||
 | 
					              </packing>
 | 
				
			||||||
 | 
					            </child>
 | 
				
			||||||
 | 
					            <child>
 | 
				
			||||||
 | 
					              <object class="GtkButton" id="ok_button">
 | 
				
			||||||
 | 
					                <property name="label">gtk-ok</property>
 | 
				
			||||||
 | 
					                <property name="visible">True</property>
 | 
				
			||||||
 | 
					                <property name="can-focus">True</property>
 | 
				
			||||||
 | 
					                <property name="can-default">True</property>
 | 
				
			||||||
 | 
					                <property name="receives-default">False</property>
 | 
				
			||||||
 | 
					                <property name="use-stock">True</property>
 | 
				
			||||||
 | 
					              </object>
 | 
				
			||||||
 | 
					              <packing>
 | 
				
			||||||
 | 
					                <property name="expand">True</property>
 | 
				
			||||||
 | 
					                <property name="fill">True</property>
 | 
				
			||||||
 | 
					                <property name="position">1</property>
 | 
				
			||||||
 | 
					              </packing>
 | 
				
			||||||
 | 
					            </child>
 | 
				
			||||||
 | 
					          </object>
 | 
				
			||||||
 | 
					          <packing>
 | 
				
			||||||
 | 
					            <property name="expand">False</property>
 | 
				
			||||||
 | 
					            <property name="fill">False</property>
 | 
				
			||||||
 | 
					            <property name="pack-type">end</property>
 | 
				
			||||||
 | 
					            <property name="position">0</property>
 | 
				
			||||||
 | 
					          </packing>
 | 
				
			||||||
 | 
					        </child>
 | 
				
			||||||
 | 
					        <child>
 | 
				
			||||||
 | 
					          <object class="GtkNotebook">
 | 
				
			||||||
 | 
					            <property name="visible">True</property>
 | 
				
			||||||
 | 
					            <property name="can-focus">True</property>
 | 
				
			||||||
 | 
					            <property name="show-border">False</property>
 | 
				
			||||||
 | 
					            <child>
 | 
				
			||||||
 | 
					              <object class="GtkBox">
 | 
				
			||||||
 | 
					                <property name="visible">True</property>
 | 
				
			||||||
 | 
					                <property name="can-focus">False</property>
 | 
				
			||||||
 | 
					                <property name="orientation">vertical</property>
 | 
				
			||||||
 | 
					                <child>
 | 
				
			||||||
 | 
					                  <object class="GtkSearchEntry">
 | 
				
			||||||
 | 
					                    <property name="visible">True</property>
 | 
				
			||||||
 | 
					                    <property name="can-focus">True</property>
 | 
				
			||||||
 | 
					                    <property name="tooltip-text" translatable="yes">Query...</property>
 | 
				
			||||||
 | 
					                    <property name="primary-icon-name">edit-find-symbolic</property>
 | 
				
			||||||
 | 
					                    <property name="primary-icon-activatable">False</property>
 | 
				
			||||||
 | 
					                    <property name="primary-icon-sensitive">False</property>
 | 
				
			||||||
 | 
					                    <property name="placeholder-text" translatable="yes">Search for file...</property>
 | 
				
			||||||
 | 
					                    <signal name="search-changed" handler="_run_find_file_query" swapped="no"/>
 | 
				
			||||||
 | 
					                  </object>
 | 
				
			||||||
 | 
					                  <packing>
 | 
				
			||||||
 | 
					                    <property name="expand">False</property>
 | 
				
			||||||
 | 
					                    <property name="fill">True</property>
 | 
				
			||||||
 | 
					                    <property name="position">0</property>
 | 
				
			||||||
 | 
					                  </packing>
 | 
				
			||||||
 | 
					                </child>
 | 
				
			||||||
 | 
					                <child>
 | 
				
			||||||
 | 
					                  <object class="GtkScrolledWindow">
 | 
				
			||||||
 | 
					                    <property name="visible">True</property>
 | 
				
			||||||
 | 
					                    <property name="can-focus">True</property>
 | 
				
			||||||
 | 
					                    <property name="hexpand">True</property>
 | 
				
			||||||
 | 
					                    <property name="vexpand">True</property>
 | 
				
			||||||
 | 
					                    <property name="shadow-type">in</property>
 | 
				
			||||||
 | 
					                    <child>
 | 
				
			||||||
 | 
					                      <object class="GtkViewport">
 | 
				
			||||||
 | 
					                        <property name="visible">True</property>
 | 
				
			||||||
 | 
					                        <property name="can-focus">False</property>
 | 
				
			||||||
 | 
					                        <child>
 | 
				
			||||||
 | 
					                          <object class="GtkBox" id="file_list">
 | 
				
			||||||
 | 
					                            <property name="visible">True</property>
 | 
				
			||||||
 | 
					                            <property name="can-focus">False</property>
 | 
				
			||||||
 | 
					                            <property name="orientation">vertical</property>
 | 
				
			||||||
 | 
					                            <property name="spacing">5</property>
 | 
				
			||||||
 | 
					                            <property name="baseline-position">top</property>
 | 
				
			||||||
 | 
					                            <child>
 | 
				
			||||||
 | 
					                              <placeholder/>
 | 
				
			||||||
 | 
					                            </child>
 | 
				
			||||||
 | 
					                          </object>
 | 
				
			||||||
 | 
					                        </child>
 | 
				
			||||||
 | 
					                      </object>
 | 
				
			||||||
 | 
					                    </child>
 | 
				
			||||||
 | 
					                  </object>
 | 
				
			||||||
 | 
					                  <packing>
 | 
				
			||||||
 | 
					                    <property name="expand">True</property>
 | 
				
			||||||
 | 
					                    <property name="fill">True</property>
 | 
				
			||||||
 | 
					                    <property name="position">1</property>
 | 
				
			||||||
 | 
					                  </packing>
 | 
				
			||||||
 | 
					                </child>
 | 
				
			||||||
 | 
					              </object>
 | 
				
			||||||
 | 
					            </child>
 | 
				
			||||||
 | 
					            <child type="tab">
 | 
				
			||||||
 | 
					              <object class="GtkLabel">
 | 
				
			||||||
 | 
					                <property name="visible">True</property>
 | 
				
			||||||
 | 
					                <property name="can-focus">False</property>
 | 
				
			||||||
 | 
					                <property name="label" translatable="yes">File Search</property>
 | 
				
			||||||
 | 
					              </object>
 | 
				
			||||||
 | 
					              <packing>
 | 
				
			||||||
 | 
					                <property name="tab-fill">False</property>
 | 
				
			||||||
 | 
					              </packing>
 | 
				
			||||||
 | 
					            </child>
 | 
				
			||||||
 | 
					            <child>
 | 
				
			||||||
 | 
					              <object class="GtkBox">
 | 
				
			||||||
 | 
					                <property name="visible">True</property>
 | 
				
			||||||
 | 
					                <property name="can-focus">False</property>
 | 
				
			||||||
 | 
					                <property name="orientation">vertical</property>
 | 
				
			||||||
 | 
					                <child>
 | 
				
			||||||
 | 
					                  <object class="GtkSearchEntry">
 | 
				
			||||||
 | 
					                    <property name="visible">True</property>
 | 
				
			||||||
 | 
					                    <property name="can-focus">True</property>
 | 
				
			||||||
 | 
					                    <property name="tooltip-text" translatable="yes">Query...</property>
 | 
				
			||||||
 | 
					                    <property name="primary-icon-name">edit-find-symbolic</property>
 | 
				
			||||||
 | 
					                    <property name="primary-icon-activatable">False</property>
 | 
				
			||||||
 | 
					                    <property name="primary-icon-sensitive">False</property>
 | 
				
			||||||
 | 
					                    <property name="placeholder-text" translatable="yes">Query string in file...</property>
 | 
				
			||||||
 | 
					                    <signal name="search-changed" handler="_run_grep_query" swapped="no"/>
 | 
				
			||||||
 | 
					                  </object>
 | 
				
			||||||
 | 
					                  <packing>
 | 
				
			||||||
 | 
					                    <property name="expand">False</property>
 | 
				
			||||||
 | 
					                    <property name="fill">True</property>
 | 
				
			||||||
 | 
					                    <property name="position">0</property>
 | 
				
			||||||
 | 
					                  </packing>
 | 
				
			||||||
 | 
					                </child>
 | 
				
			||||||
 | 
					                <child>
 | 
				
			||||||
 | 
					                  <object class="GtkScrolledWindow">
 | 
				
			||||||
 | 
					                    <property name="visible">True</property>
 | 
				
			||||||
 | 
					                    <property name="can-focus">True</property>
 | 
				
			||||||
 | 
					                    <property name="hexpand">True</property>
 | 
				
			||||||
 | 
					                    <property name="vexpand">True</property>
 | 
				
			||||||
 | 
					                    <property name="shadow-type">in</property>
 | 
				
			||||||
 | 
					                    <child>
 | 
				
			||||||
 | 
					                      <object class="GtkViewport">
 | 
				
			||||||
 | 
					                        <property name="visible">True</property>
 | 
				
			||||||
 | 
					                        <property name="can-focus">False</property>
 | 
				
			||||||
 | 
					                        <child>
 | 
				
			||||||
 | 
					                          <object class="GtkBox" id="grep_list">
 | 
				
			||||||
 | 
					                            <property name="visible">True</property>
 | 
				
			||||||
 | 
					                            <property name="can-focus">False</property>
 | 
				
			||||||
 | 
					                            <property name="orientation">vertical</property>
 | 
				
			||||||
 | 
					                            <property name="spacing">5</property>
 | 
				
			||||||
 | 
					                            <property name="baseline-position">top</property>
 | 
				
			||||||
 | 
					                            <child>
 | 
				
			||||||
 | 
					                              <placeholder/>
 | 
				
			||||||
 | 
					                            </child>
 | 
				
			||||||
 | 
					                          </object>
 | 
				
			||||||
 | 
					                        </child>
 | 
				
			||||||
 | 
					                      </object>
 | 
				
			||||||
 | 
					                    </child>
 | 
				
			||||||
 | 
					                  </object>
 | 
				
			||||||
 | 
					                  <packing>
 | 
				
			||||||
 | 
					                    <property name="expand">True</property>
 | 
				
			||||||
 | 
					                    <property name="fill">True</property>
 | 
				
			||||||
 | 
					                    <property name="position">1</property>
 | 
				
			||||||
 | 
					                  </packing>
 | 
				
			||||||
 | 
					                </child>
 | 
				
			||||||
 | 
					              </object>
 | 
				
			||||||
 | 
					              <packing>
 | 
				
			||||||
 | 
					                <property name="position">1</property>
 | 
				
			||||||
 | 
					              </packing>
 | 
				
			||||||
 | 
					            </child>
 | 
				
			||||||
 | 
					            <child type="tab">
 | 
				
			||||||
 | 
					              <object class="GtkLabel">
 | 
				
			||||||
 | 
					                <property name="visible">True</property>
 | 
				
			||||||
 | 
					                <property name="can-focus">False</property>
 | 
				
			||||||
 | 
					                <property name="label" translatable="yes">Grep Search</property>
 | 
				
			||||||
 | 
					              </object>
 | 
				
			||||||
 | 
					              <packing>
 | 
				
			||||||
 | 
					                <property name="position">1</property>
 | 
				
			||||||
 | 
					                <property name="tab-fill">False</property>
 | 
				
			||||||
 | 
					              </packing>
 | 
				
			||||||
 | 
					            </child>
 | 
				
			||||||
 | 
					            <child>
 | 
				
			||||||
 | 
					              <placeholder/>
 | 
				
			||||||
 | 
					            </child>
 | 
				
			||||||
 | 
					            <child type="tab">
 | 
				
			||||||
 | 
					              <placeholder/>
 | 
				
			||||||
 | 
					            </child>
 | 
				
			||||||
 | 
					          </object>
 | 
				
			||||||
 | 
					          <packing>
 | 
				
			||||||
 | 
					            <property name="expand">True</property>
 | 
				
			||||||
 | 
					            <property name="fill">True</property>
 | 
				
			||||||
 | 
					            <property name="position">1</property>
 | 
				
			||||||
 | 
					          </packing>
 | 
				
			||||||
 | 
					        </child>
 | 
				
			||||||
 | 
					      </object>
 | 
				
			||||||
 | 
					    </child>
 | 
				
			||||||
 | 
					    <action-widgets>
 | 
				
			||||||
 | 
					      <action-widget response="-6">cancel_button</action-widget>
 | 
				
			||||||
 | 
					      <action-widget response="-5">ok_button</action-widget>
 | 
				
			||||||
 | 
					    </action-widgets>
 | 
				
			||||||
 | 
					  </object>
 | 
				
			||||||
 | 
					</interface>
 | 
				
			||||||
							
								
								
									
										3
									
								
								plugins/template/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								plugins/template/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					    Pligin Module
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
							
								
								
									
										3
									
								
								plugins/template/__main__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								plugins/template/__main__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					    Pligin Package
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
							
								
								
									
										13
									
								
								plugins/template/manifest.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								plugins/template/manifest.json
									
									
									
									
									
										Normal file
									
								
							@@ -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:<Control>f"]
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
# Python imports
 | 
					# Python imports
 | 
				
			||||||
import os, threading, subprocess, time
 | 
					import os, threading, subprocess, time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Gtk imports
 | 
					# Lib imports
 | 
				
			||||||
import gi
 | 
					import gi
 | 
				
			||||||
gi.require_version('Gtk', '3.0')
 | 
					gi.require_version('Gtk', '3.0')
 | 
				
			||||||
from gi.repository import Gtk
 | 
					from gi.repository import Gtk
 | 
				
			||||||
@@ -24,21 +24,10 @@ def daemon_threaded(fn):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Manifest:
 | 
					class Plugin:
 | 
				
			||||||
    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:<Control>f"]
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Plugin(Manifest):
 | 
					 | 
				
			||||||
    def __init__(self):
 | 
					    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_system      = None
 | 
				
			||||||
        self._event_sleep_time  = .5
 | 
					        self._event_sleep_time  = .5
 | 
				
			||||||
        self._event_message     = None
 | 
					        self._event_message     = None
 | 
				
			||||||
@@ -58,7 +47,6 @@ class Plugin(Manifest):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    def send_message(self, widget=None, eve=None):
 | 
					    def send_message(self, widget=None, eve=None):
 | 
				
			||||||
        message = "Hello, World!"
 | 
					        message = "Hello, World!"
 | 
				
			||||||
        print("here")
 | 
					 | 
				
			||||||
        self._event_system.push_gui_event([self.name, "display_message", ("warning", message, None)])
 | 
					        self._event_system.push_gui_event([self.name, "display_message", ("warning", message, None)])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										3
									
								
								plugins/vod_thumbnailer/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								plugins/vod_thumbnailer/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					    Pligin Module
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
							
								
								
									
										3
									
								
								plugins/vod_thumbnailer/__main__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								plugins/vod_thumbnailer/__main__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					    Pligin Package
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
							
								
								
									
										12
									
								
								plugins/vod_thumbnailer/manifest.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								plugins/vod_thumbnailer/manifest.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					    "manifest": {
 | 
				
			||||||
 | 
					        "name": "VOD Thumbnailer",
 | 
				
			||||||
 | 
					        "author": "ITDominator",
 | 
				
			||||||
 | 
					        "version": "0.0.1",
 | 
				
			||||||
 | 
					        "support": "",
 | 
				
			||||||
 | 
					        "requests": {
 | 
				
			||||||
 | 
					            "ui_target": "context_menu",
 | 
				
			||||||
 | 
					            "pass_fm_events": "true"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										165
									
								
								plugins/vod_thumbnailer/plugin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								plugins/vod_thumbnailer/plugin.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,165 @@
 | 
				
			|||||||
 | 
					# 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 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
 | 
				
			||||||
 | 
					        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))
 | 
				
			||||||
							
								
								
									
										219
									
								
								plugins/vod_thumbnailer/re_thumbnailer.glade
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										219
									
								
								plugins/vod_thumbnailer/re_thumbnailer.glade
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,219 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="UTF-8"?>
 | 
				
			||||||
 | 
					<!-- Generated with glade 3.38.2 -->
 | 
				
			||||||
 | 
					<interface>
 | 
				
			||||||
 | 
					  <requires lib="gtk+" version="3.16"/>
 | 
				
			||||||
 | 
					  <object class="GtkDialog" id="thumbnailer_dialog">
 | 
				
			||||||
 | 
					    <property name="can-focus">False</property>
 | 
				
			||||||
 | 
					    <property name="border-width">6</property>
 | 
				
			||||||
 | 
					    <property name="title" translatable="yes">VOD Thumbnailer</property>
 | 
				
			||||||
 | 
					    <property name="modal">True</property>
 | 
				
			||||||
 | 
					    <property name="window-position">center-on-parent</property>
 | 
				
			||||||
 | 
					    <property name="default-width">420</property>
 | 
				
			||||||
 | 
					    <property name="destroy-with-parent">True</property>
 | 
				
			||||||
 | 
					    <property name="type-hint">dialog</property>
 | 
				
			||||||
 | 
					    <property name="skip-taskbar-hint">True</property>
 | 
				
			||||||
 | 
					    <property name="skip-pager-hint">True</property>
 | 
				
			||||||
 | 
					    <property name="gravity">center</property>
 | 
				
			||||||
 | 
					    <child internal-child="vbox">
 | 
				
			||||||
 | 
					      <object class="GtkBox" id="dialog_vbox">
 | 
				
			||||||
 | 
					        <property name="visible">True</property>
 | 
				
			||||||
 | 
					        <property name="can-focus">False</property>
 | 
				
			||||||
 | 
					        <property name="spacing">12</property>
 | 
				
			||||||
 | 
					        <child internal-child="action_area">
 | 
				
			||||||
 | 
					          <object class="GtkButtonBox" id="dialog_action_area">
 | 
				
			||||||
 | 
					            <property name="visible">True</property>
 | 
				
			||||||
 | 
					            <property name="can-focus">False</property>
 | 
				
			||||||
 | 
					            <property name="layout-style">end</property>
 | 
				
			||||||
 | 
					            <child>
 | 
				
			||||||
 | 
					              <object class="GtkButton" id="cancel_button">
 | 
				
			||||||
 | 
					                <property name="label">gtk-cancel</property>
 | 
				
			||||||
 | 
					                <property name="visible">True</property>
 | 
				
			||||||
 | 
					                <property name="can-focus">True</property>
 | 
				
			||||||
 | 
					                <property name="can-default">True</property>
 | 
				
			||||||
 | 
					                <property name="receives-default">False</property>
 | 
				
			||||||
 | 
					                <property name="use-stock">True</property>
 | 
				
			||||||
 | 
					              </object>
 | 
				
			||||||
 | 
					              <packing>
 | 
				
			||||||
 | 
					                <property name="expand">True</property>
 | 
				
			||||||
 | 
					                <property name="fill">True</property>
 | 
				
			||||||
 | 
					                <property name="position">0</property>
 | 
				
			||||||
 | 
					              </packing>
 | 
				
			||||||
 | 
					            </child>
 | 
				
			||||||
 | 
					          </object>
 | 
				
			||||||
 | 
					          <packing>
 | 
				
			||||||
 | 
					            <property name="expand">False</property>
 | 
				
			||||||
 | 
					            <property name="fill">False</property>
 | 
				
			||||||
 | 
					            <property name="pack-type">end</property>
 | 
				
			||||||
 | 
					            <property name="position">0</property>
 | 
				
			||||||
 | 
					          </packing>
 | 
				
			||||||
 | 
					        </child>
 | 
				
			||||||
 | 
					        <child>
 | 
				
			||||||
 | 
					          <object class="GtkBox">
 | 
				
			||||||
 | 
					            <property name="visible">True</property>
 | 
				
			||||||
 | 
					            <property name="can-focus">False</property>
 | 
				
			||||||
 | 
					            <property name="orientation">vertical</property>
 | 
				
			||||||
 | 
					            <child>
 | 
				
			||||||
 | 
					              <object class="GtkImage" id="thumbnail_preview_img">
 | 
				
			||||||
 | 
					                <property name="visible">True</property>
 | 
				
			||||||
 | 
					                <property name="can-focus">False</property>
 | 
				
			||||||
 | 
					                <property name="stock">gtk-missing-image</property>
 | 
				
			||||||
 | 
					                <property name="icon_size">6</property>
 | 
				
			||||||
 | 
					              </object>
 | 
				
			||||||
 | 
					              <packing>
 | 
				
			||||||
 | 
					                <property name="expand">True</property>
 | 
				
			||||||
 | 
					                <property name="fill">True</property>
 | 
				
			||||||
 | 
					                <property name="position">0</property>
 | 
				
			||||||
 | 
					              </packing>
 | 
				
			||||||
 | 
					            </child>
 | 
				
			||||||
 | 
					            <child>
 | 
				
			||||||
 | 
					              <placeholder/>
 | 
				
			||||||
 | 
					            </child>
 | 
				
			||||||
 | 
					            <child>
 | 
				
			||||||
 | 
					              <object class="GtkTable" id="general_table">
 | 
				
			||||||
 | 
					                <property name="visible">True</property>
 | 
				
			||||||
 | 
					                <property name="can-focus">False</property>
 | 
				
			||||||
 | 
					                <property name="border-width">4</property>
 | 
				
			||||||
 | 
					                <property name="n-rows">4</property>
 | 
				
			||||||
 | 
					                <property name="n-columns">2</property>
 | 
				
			||||||
 | 
					                <property name="column-spacing">12</property>
 | 
				
			||||||
 | 
					                <property name="row-spacing">6</property>
 | 
				
			||||||
 | 
					                <child>
 | 
				
			||||||
 | 
					                  <object class="GtkLabel">
 | 
				
			||||||
 | 
					                    <property name="visible">True</property>
 | 
				
			||||||
 | 
					                    <property name="can-focus">False</property>
 | 
				
			||||||
 | 
					                    <property name="label" translatable="yes"><b>File _Name:</b></property>
 | 
				
			||||||
 | 
					                    <property name="use-markup">True</property>
 | 
				
			||||||
 | 
					                    <property name="use-underline">True</property>
 | 
				
			||||||
 | 
					                    <property name="mnemonic-widget">file_name</property>
 | 
				
			||||||
 | 
					                    <property name="xalign">0</property>
 | 
				
			||||||
 | 
					                  </object>
 | 
				
			||||||
 | 
					                  <packing>
 | 
				
			||||||
 | 
					                    <property name="x-options">GTK_FILL</property>
 | 
				
			||||||
 | 
					                    <property name="y-options"/>
 | 
				
			||||||
 | 
					                  </packing>
 | 
				
			||||||
 | 
					                </child>
 | 
				
			||||||
 | 
					                <child>
 | 
				
			||||||
 | 
					                  <object class="GtkEntry" id="file_name">
 | 
				
			||||||
 | 
					                    <property name="visible">True</property>
 | 
				
			||||||
 | 
					                    <property name="can-focus">True</property>
 | 
				
			||||||
 | 
					                    <property name="editable">False</property>
 | 
				
			||||||
 | 
					                  </object>
 | 
				
			||||||
 | 
					                  <packing>
 | 
				
			||||||
 | 
					                    <property name="left-attach">1</property>
 | 
				
			||||||
 | 
					                    <property name="right-attach">2</property>
 | 
				
			||||||
 | 
					                    <property name="y-options"/>
 | 
				
			||||||
 | 
					                  </packing>
 | 
				
			||||||
 | 
					                </child>
 | 
				
			||||||
 | 
					                <child>
 | 
				
			||||||
 | 
					                  <object class="GtkLabel">
 | 
				
			||||||
 | 
					                    <property name="visible">True</property>
 | 
				
			||||||
 | 
					                    <property name="can-focus">False</property>
 | 
				
			||||||
 | 
					                    <property name="label" translatable="yes"><b>_Location:</b></property>
 | 
				
			||||||
 | 
					                    <property name="use-markup">True</property>
 | 
				
			||||||
 | 
					                    <property name="use-underline">True</property>
 | 
				
			||||||
 | 
					                    <property name="mnemonic-widget">file_location</property>
 | 
				
			||||||
 | 
					                    <property name="xalign">0</property>
 | 
				
			||||||
 | 
					                  </object>
 | 
				
			||||||
 | 
					                  <packing>
 | 
				
			||||||
 | 
					                    <property name="top-attach">1</property>
 | 
				
			||||||
 | 
					                    <property name="bottom-attach">2</property>
 | 
				
			||||||
 | 
					                    <property name="x-options">GTK_FILL</property>
 | 
				
			||||||
 | 
					                    <property name="y-options"/>
 | 
				
			||||||
 | 
					                  </packing>
 | 
				
			||||||
 | 
					                </child>
 | 
				
			||||||
 | 
					                <child>
 | 
				
			||||||
 | 
					                  <object class="GtkEntry" id="file_location">
 | 
				
			||||||
 | 
					                    <property name="visible">True</property>
 | 
				
			||||||
 | 
					                    <property name="can-focus">True</property>
 | 
				
			||||||
 | 
					                    <property name="editable">False</property>
 | 
				
			||||||
 | 
					                  </object>
 | 
				
			||||||
 | 
					                  <packing>
 | 
				
			||||||
 | 
					                    <property name="left-attach">1</property>
 | 
				
			||||||
 | 
					                    <property name="right-attach">2</property>
 | 
				
			||||||
 | 
					                    <property name="top-attach">1</property>
 | 
				
			||||||
 | 
					                    <property name="bottom-attach">2</property>
 | 
				
			||||||
 | 
					                    <property name="x-options">GTK_FILL</property>
 | 
				
			||||||
 | 
					                    <property name="y-options"/>
 | 
				
			||||||
 | 
					                  </packing>
 | 
				
			||||||
 | 
					                </child>
 | 
				
			||||||
 | 
					                <child>
 | 
				
			||||||
 | 
					                  <object class="GtkButton">
 | 
				
			||||||
 | 
					                    <property name="label" translatable="yes">Regenerate</property>
 | 
				
			||||||
 | 
					                    <property name="visible">True</property>
 | 
				
			||||||
 | 
					                    <property name="can-focus">True</property>
 | 
				
			||||||
 | 
					                    <property name="receives-default">True</property>
 | 
				
			||||||
 | 
					                    <signal name="released" handler="_regenerate_thumbnail" swapped="no"/>
 | 
				
			||||||
 | 
					                  </object>
 | 
				
			||||||
 | 
					                  <packing>
 | 
				
			||||||
 | 
					                    <property name="left-attach">1</property>
 | 
				
			||||||
 | 
					                    <property name="right-attach">2</property>
 | 
				
			||||||
 | 
					                    <property name="top-attach">3</property>
 | 
				
			||||||
 | 
					                    <property name="bottom-attach">4</property>
 | 
				
			||||||
 | 
					                  </packing>
 | 
				
			||||||
 | 
					                </child>
 | 
				
			||||||
 | 
					                <child>
 | 
				
			||||||
 | 
					                  <object class="GtkButton">
 | 
				
			||||||
 | 
					                    <property name="label" translatable="yes">Use Selected</property>
 | 
				
			||||||
 | 
					                    <property name="visible">True</property>
 | 
				
			||||||
 | 
					                    <property name="can-focus">True</property>
 | 
				
			||||||
 | 
					                    <property name="receives-default">True</property>
 | 
				
			||||||
 | 
					                    <signal name="released" handler="_use_selected_thumbnail" swapped="no"/>
 | 
				
			||||||
 | 
					                  </object>
 | 
				
			||||||
 | 
					                  <packing>
 | 
				
			||||||
 | 
					                    <property name="top-attach">3</property>
 | 
				
			||||||
 | 
					                    <property name="bottom-attach">4</property>
 | 
				
			||||||
 | 
					                  </packing>
 | 
				
			||||||
 | 
					                </child>
 | 
				
			||||||
 | 
					                <child>
 | 
				
			||||||
 | 
					                  <object class="GtkLabel" id="hash">
 | 
				
			||||||
 | 
					                    <property name="visible">True</property>
 | 
				
			||||||
 | 
					                    <property name="can-focus">False</property>
 | 
				
			||||||
 | 
					                    <property name="label" translatable="yes"><b>_Thumbnail Hash:</b></property>
 | 
				
			||||||
 | 
					                    <property name="use-markup">True</property>
 | 
				
			||||||
 | 
					                    <property name="use-underline">True</property>
 | 
				
			||||||
 | 
					                    <property name="mnemonic-widget">file_location</property>
 | 
				
			||||||
 | 
					                    <property name="xalign">0</property>
 | 
				
			||||||
 | 
					                  </object>
 | 
				
			||||||
 | 
					                  <packing>
 | 
				
			||||||
 | 
					                    <property name="top-attach">2</property>
 | 
				
			||||||
 | 
					                    <property name="bottom-attach">3</property>
 | 
				
			||||||
 | 
					                    <property name="x-options">GTK_FILL</property>
 | 
				
			||||||
 | 
					                    <property name="y-options"/>
 | 
				
			||||||
 | 
					                  </packing>
 | 
				
			||||||
 | 
					                </child>
 | 
				
			||||||
 | 
					                <child>
 | 
				
			||||||
 | 
					                  <object class="GtkEntry" id="file_hash">
 | 
				
			||||||
 | 
					                    <property name="visible">True</property>
 | 
				
			||||||
 | 
					                    <property name="can-focus">True</property>
 | 
				
			||||||
 | 
					                    <property name="editable">False</property>
 | 
				
			||||||
 | 
					                  </object>
 | 
				
			||||||
 | 
					                  <packing>
 | 
				
			||||||
 | 
					                    <property name="left-attach">1</property>
 | 
				
			||||||
 | 
					                    <property name="right-attach">2</property>
 | 
				
			||||||
 | 
					                    <property name="top-attach">2</property>
 | 
				
			||||||
 | 
					                    <property name="bottom-attach">3</property>
 | 
				
			||||||
 | 
					                    <property name="x-options">GTK_FILL</property>
 | 
				
			||||||
 | 
					                    <property name="y-options"/>
 | 
				
			||||||
 | 
					                  </packing>
 | 
				
			||||||
 | 
					                </child>
 | 
				
			||||||
 | 
					              </object>
 | 
				
			||||||
 | 
					              <packing>
 | 
				
			||||||
 | 
					                <property name="expand">False</property>
 | 
				
			||||||
 | 
					                <property name="fill">True</property>
 | 
				
			||||||
 | 
					                <property name="position">2</property>
 | 
				
			||||||
 | 
					              </packing>
 | 
				
			||||||
 | 
					            </child>
 | 
				
			||||||
 | 
					          </object>
 | 
				
			||||||
 | 
					          <packing>
 | 
				
			||||||
 | 
					            <property name="expand">True</property>
 | 
				
			||||||
 | 
					            <property name="fill">True</property>
 | 
				
			||||||
 | 
					            <property name="position">1</property>
 | 
				
			||||||
 | 
					          </packing>
 | 
				
			||||||
 | 
					        </child>
 | 
				
			||||||
 | 
					      </object>
 | 
				
			||||||
 | 
					    </child>
 | 
				
			||||||
 | 
					    <action-widgets>
 | 
				
			||||||
 | 
					      <action-widget response="-6">cancel_button</action-widget>
 | 
				
			||||||
 | 
					    </action-widgets>
 | 
				
			||||||
 | 
					  </object>
 | 
				
			||||||
 | 
					</interface>
 | 
				
			||||||
							
								
								
									
										3
									
								
								plugins/youtube_download/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								plugins/youtube_download/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					    Pligin Module
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
							
								
								
									
										3
									
								
								plugins/youtube_download/__main__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								plugins/youtube_download/__main__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					    Pligin Package
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
@@ -10,7 +10,6 @@
 | 
				
			|||||||
function main() {
 | 
					function main() {
 | 
				
			||||||
    cd "$(dirname "")"
 | 
					    cd "$(dirname "")"
 | 
				
			||||||
    echo "Working Dir: " $(pwd)
 | 
					    echo "Working Dir: " $(pwd)
 | 
				
			||||||
    source "/home/abaddon/Portable_Apps/py-venvs/yt-dlp-venv/venv/bin/activate"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    LINK=`xclip -selection clipboard -o`
 | 
					    LINK=`xclip -selection clipboard -o`
 | 
				
			||||||
    yt-dlp --write-sub --embed-sub --sub-langs en -o "${1}/%(title)s.%(ext)s" "${LINK}"
 | 
					    yt-dlp --write-sub --embed-sub --sub-langs en -o "${1}/%(title)s.%(ext)s" "${LINK}"
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										12
									
								
								plugins/youtube_download/manifest.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								plugins/youtube_download/manifest.json
									
									
									
									
									
										Normal file
									
								
							@@ -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"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
# Python imports
 | 
					# Python imports
 | 
				
			||||||
import os, threading, subprocess, time
 | 
					import os, threading, subprocess, time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Gtk imports
 | 
					# Lib imports
 | 
				
			||||||
import gi
 | 
					import gi
 | 
				
			||||||
gi.require_version('Gtk', '3.0')
 | 
					gi.require_version('Gtk', '3.0')
 | 
				
			||||||
from gi.repository import Gtk
 | 
					from gi.repository import Gtk
 | 
				
			||||||
@@ -24,21 +24,10 @@ def daemon_threaded(fn):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Manifest:
 | 
					class Plugin:
 | 
				
			||||||
    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):
 | 
					 | 
				
			||||||
    def __init__(self):
 | 
					    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_system     = None
 | 
				
			||||||
        self._event_sleep_time = .5
 | 
					        self._event_sleep_time = .5
 | 
				
			||||||
        self._event_message    = None
 | 
					        self._event_message    = None
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										17
									
								
								src/versions/solarfm-0.0.1/SolarFM/create-binary.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										17
									
								
								src/versions/solarfm-0.0.1/SolarFM/create-binary.sh
									
									
									
									
									
										Executable file
									
								
							@@ -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 "$@";
 | 
				
			||||||
@@ -8,11 +8,10 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function main() {
 | 
					function main() {
 | 
				
			||||||
    SCRIPTPATH="$( cd "$(dirname "")" >/dev/null 2>&1 ; pwd -P )"
 | 
					    cd "$(dirname "")"
 | 
				
			||||||
    cd "${SCRIPTPATH}"
 | 
					 | 
				
			||||||
    echo "Working Dir: " $(pwd)
 | 
					    echo "Working Dir: " $(pwd)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    source "/home/abaddon/Portable_Apps/py-venvs/flask-apps-venv/venv/bin/activate"
 | 
					    source "/home/abaddon/Portable_Apps/py-venvs/gtk-apps-venv/venv/bin/activate"
 | 
				
			||||||
    python ./solarfm
 | 
					    python -m nuitka --follow-imports --standalone --linux-onefile-icon="/home/abaddon/.config/solarfm/solarfm.png"  solarfm/__main__.py
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
main "$@";
 | 
					main "$@";
 | 
				
			||||||
@@ -1,67 +1,43 @@
 | 
				
			|||||||
# Python imports
 | 
					# Python imports
 | 
				
			||||||
import builtins
 | 
					import builtins, threading
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Lib imports
 | 
					# Lib imports
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Application imports
 | 
					# Application imports
 | 
				
			||||||
from utils.ipc_server import IPCServer
 | 
					from utils.event_system import EventSystem
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class EventSystem(IPCServer):
 | 
					# NOTE: Threads WILL NOT die with parent's destruction.
 | 
				
			||||||
    """ Inheret IPCServerMixin. Create an pub/sub systems. """
 | 
					def threaded_wrapper(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_wrapper(fn):
 | 
				
			||||||
 | 
					    def wrapper(*args, **kwargs):
 | 
				
			||||||
 | 
					        threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start()
 | 
				
			||||||
 | 
					    return wrapper
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class EndpointRegistry():
 | 
				
			||||||
    def __init__(self):
 | 
					    def __init__(self):
 | 
				
			||||||
        super(EventSystem, self).__init__()
 | 
					        self._endpoints = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # NOTE: The format used is list of ['who', target, (data,)] Where:
 | 
					    def register(self, rule, **options):
 | 
				
			||||||
        #             who is the sender or target ID and is used for context and control flow,
 | 
					        def decorator(f):
 | 
				
			||||||
        #             method_target is the method to call,
 | 
					            self._endpoints[rule] = f
 | 
				
			||||||
        #             data is the method parameters OR message data to give
 | 
					            return f
 | 
				
			||||||
        #       Where data may be any kind of data
 | 
					 | 
				
			||||||
        self._gui_events    = []
 | 
					 | 
				
			||||||
        self._module_events = []
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return decorator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_endpoints(self):
 | 
				
			||||||
 | 
					        return self._endpoints
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # 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()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -69,6 +45,10 @@ class EventSystem(IPCServer):
 | 
				
			|||||||
# __builtins__.update({"event_system": Builtins()})
 | 
					# __builtins__.update({"event_system": Builtins()})
 | 
				
			||||||
builtins.app_name          = "SolarFM"
 | 
					builtins.app_name          = "SolarFM"
 | 
				
			||||||
builtins.event_system      = EventSystem()
 | 
					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.event_sleep_time  = 0.05
 | 
				
			||||||
builtins.trace_debug       = False
 | 
					builtins.trace_debug       = False
 | 
				
			||||||
builtins.debug             = False
 | 
					builtins.debug             = False
 | 
				
			||||||
 | 
					builtins.app_settings      = None
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,31 +4,33 @@ import os, inspect, time
 | 
				
			|||||||
# Lib imports
 | 
					# Lib imports
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Application imports
 | 
					# Application imports
 | 
				
			||||||
 | 
					from __builtins__ import *
 | 
				
			||||||
 | 
					from utils.ipc_server import IPCServer
 | 
				
			||||||
from utils.settings import Settings
 | 
					from utils.settings import Settings
 | 
				
			||||||
from core.controller import Controller
 | 
					from core.controller import Controller
 | 
				
			||||||
from __builtins__ import EventSystem
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Application(IPCServer):
 | 
				
			||||||
class Application(EventSystem):
 | 
					 | 
				
			||||||
    """ Create Settings and Controller classes. Bind signal to Builder. Inherit from Builtins to bind global methods and classes. """
 | 
					    """ Create Settings and Controller classes. Bind signal to Builder. Inherit from Builtins to bind global methods and classes. """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, args, unknownargs):
 | 
					    def __init__(self, args, unknownargs):
 | 
				
			||||||
 | 
					        super(Application, self).__init__()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not trace_debug:
 | 
					        if not trace_debug:
 | 
				
			||||||
            event_system.create_ipc_listener()
 | 
					            self.create_ipc_listener()
 | 
				
			||||||
            time.sleep(0.05)
 | 
					            time.sleep(0.05)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if not event_system.is_ipc_alive:
 | 
					            if not self.is_ipc_alive:
 | 
				
			||||||
                if unknownargs:
 | 
					                if unknownargs:
 | 
				
			||||||
                    for arg in unknownargs:
 | 
					                    for arg in unknownargs:
 | 
				
			||||||
                        if os.path.isdir(arg):
 | 
					                        if os.path.isdir(arg):
 | 
				
			||||||
                            message = f"FILE|{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):
 | 
					                if args.new_tab and os.path.isdir(args.new_tab):
 | 
				
			||||||
                    message = f"FILE|{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")
 | 
					                raise Exception("IPC Server Exists: Will send path(s) to it and close...\nNote: If no fm exists, remove /tmp/solarfm-ipc.sock")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
# Python imports
 | 
					# Python imports
 | 
				
			||||||
import os, gc, threading, time
 | 
					import os, gc, time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Lib imports
 | 
					# Lib imports
 | 
				
			||||||
import gi
 | 
					import gi
 | 
				
			||||||
@@ -14,19 +14,6 @@ from .signals.keyboard_signals_mixin import KeyboardSignalsMixin
 | 
				
			|||||||
from .controller_data import Controller_Data
 | 
					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):
 | 
					class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMixin, Controller_Data):
 | 
				
			||||||
@@ -56,7 +43,6 @@ class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMi
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def tear_down(self, widget=None, eve=None):
 | 
					    def tear_down(self, widget=None, eve=None):
 | 
				
			||||||
        event_system.send_ipc_message("close server")
 | 
					 | 
				
			||||||
        self.fm_controller.save_state()
 | 
					        self.fm_controller.save_state()
 | 
				
			||||||
        time.sleep(event_sleep_time)
 | 
					        time.sleep(event_sleep_time)
 | 
				
			||||||
        Gtk.main_quit()
 | 
					        Gtk.main_quit()
 | 
				
			||||||
@@ -177,23 +163,28 @@ class Controller(UIMixin, KeyboardSignalsMixin, IPCSignalsMixin, ExceptionHookMi
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @endpoint_registry.register(rule="go_home")
 | 
				
			||||||
    def go_home(self, widget=None, eve=None):
 | 
					    def go_home(self, widget=None, eve=None):
 | 
				
			||||||
        self.builder.get_object("go_home").released()
 | 
					        self.builder.get_object("go_home").released()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @endpoint_registry.register(rule="refresh_tab")
 | 
				
			||||||
    def refresh_tab(self, widget=None, eve=None):
 | 
					    def refresh_tab(self, widget=None, eve=None):
 | 
				
			||||||
        self.builder.get_object("refresh_tab").released()
 | 
					        self.builder.get_object("refresh_tab").released()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @endpoint_registry.register(rule="go_up")
 | 
				
			||||||
    def go_up(self, widget=None, eve=None):
 | 
					    def go_up(self, widget=None, eve=None):
 | 
				
			||||||
        self.builder.get_object("go_up").released()
 | 
					        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):
 | 
					    def grab_focus_path_entry(self, widget=None, eve=None):
 | 
				
			||||||
        self.builder.get_object("path_entry").grab_focus()
 | 
					        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):
 | 
					    def tggl_top_main_menubar(self, widget=None, eve=None):
 | 
				
			||||||
        top_main_menubar = self.builder.get_object("top_main_menubar")
 | 
					        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()
 | 
					        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):
 | 
					    def open_terminal(self, widget=None, eve=None):
 | 
				
			||||||
        wid, tid = self.fm_controller.get_active_wid_and_tid()
 | 
					        wid, tid = self.fm_controller.get_active_wid_and_tid()
 | 
				
			||||||
        tab      = self.get_fm_window(wid).get_tab_by_id(tid)
 | 
					        tab      = self.get_fm_window(wid).get_tab_by_id(tid)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -120,7 +120,7 @@ class Controller_Data:
 | 
				
			|||||||
        self.warning_color      = self.settings.get_warning_color()
 | 
					        self.warning_color      = self.settings.get_warning_color()
 | 
				
			||||||
        self.error_color        = self.settings.get_error_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)
 | 
					        self.window.connect("delete-event", self.tear_down)
 | 
				
			||||||
        GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, self.tear_down)
 | 
					        GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, self.tear_down)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
# Python imports
 | 
					# Python imports
 | 
				
			||||||
import traceback, threading, time
 | 
					import traceback, time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Lib imports
 | 
					# Lib imports
 | 
				
			||||||
import gi
 | 
					import gi
 | 
				
			||||||
@@ -9,10 +9,6 @@ from gi.repository import Gtk, GLib
 | 
				
			|||||||
# Application imports
 | 
					# Application imports
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def threaded(fn):
 | 
					 | 
				
			||||||
    def wrapper(*args, **kwargs):
 | 
					 | 
				
			||||||
        threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start()
 | 
					 | 
				
			||||||
    return wrapper
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ExceptionHookMixin:
 | 
					class ExceptionHookMixin:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
# Python imports
 | 
					# Python imports
 | 
				
			||||||
import os, threading, subprocess, time
 | 
					import os
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Lib imports
 | 
					# Lib imports
 | 
				
			||||||
import gi
 | 
					import gi
 | 
				
			||||||
@@ -11,12 +11,6 @@ from gi.repository import Gtk, Gdk, GLib, Gio, GdkPixbuf
 | 
				
			|||||||
# Application imports
 | 
					# 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...
 | 
					# NOTE: Consider trying to use Gtk.TreeView with css that turns it into a grid...
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
# Python imports
 | 
					# Python imports
 | 
				
			||||||
import os, time, threading, shlex
 | 
					import os, time, shlex
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Lib imports
 | 
					# Lib imports
 | 
				
			||||||
import gi
 | 
					import gi
 | 
				
			||||||
@@ -9,10 +9,6 @@ from gi.repository import Gtk, GObject, GLib, Gio
 | 
				
			|||||||
# Application imports
 | 
					# Application imports
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def threaded(fn):
 | 
					 | 
				
			||||||
    def wrapper(*args, **kwargs):
 | 
					 | 
				
			||||||
        threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start()
 | 
					 | 
				
			||||||
    return wrapper
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class WidgetFileActionMixin:
 | 
					class WidgetFileActionMixin:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
@@ -8,17 +8,9 @@ gi.require_version('Gtk', '3.0')
 | 
				
			|||||||
from gi.repository import Gtk, Gio
 | 
					from gi.repository import Gtk, Gio
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Application imports
 | 
					# 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:
 | 
					class Plugins:
 | 
				
			||||||
@@ -28,7 +20,7 @@ class Plugins:
 | 
				
			|||||||
        self._settings            = settings
 | 
					        self._settings            = settings
 | 
				
			||||||
        self._builder             = self._settings.get_builder()
 | 
					        self._builder             = self._settings.get_builder()
 | 
				
			||||||
        self._plugins_path        = self._settings.get_plugins_path()
 | 
					        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._plugins_dir_watcher = None
 | 
				
			||||||
        self._plugin_collection   = []
 | 
					        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)]:
 | 
					        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:
 | 
					            try:
 | 
				
			||||||
                target = join(path, "plugin.py")
 | 
					                target   = join(path, "plugin.py")
 | 
				
			||||||
 | 
					                manifest = ManifestProcessor(path, self._builder)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if not os.path.exists(target):
 | 
					                if not os.path.exists(target):
 | 
				
			||||||
                    raise Exception("Invalid Plugin Structure: Plugin doesn't have 'plugin.py'. Aboarting load...")
 | 
					                    raise Exception("Invalid Plugin Structure: Plugin doesn't have 'plugin.py'. Aboarting load...")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                module       = self.load_plugin_module(path, folder, target)
 | 
					                plugin, loading_data = manifest.get_loading_data()
 | 
				
			||||||
                plugin       = self.collect_info(module, path)
 | 
					                module               = self.load_plugin_module(path, folder, target)
 | 
				
			||||||
                loading_data = self.parse_requests(plugin)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                self.execute_plugin(module, plugin, loading_data)
 | 
					                self.execute_plugin(module, plugin, loading_data)
 | 
				
			||||||
            except Exception as e:
 | 
					            except Exception as e:
 | 
				
			||||||
                print(f"Malformed Plugin: Not loading -->: '{folder}' !")
 | 
					                print(f"Malformed Plugin: Not loading -->: '{folder}' !")
 | 
				
			||||||
                traceback.print_exc() 
 | 
					                traceback.print_exc() 
 | 
				
			||||||
                # if debug:
 | 
					 | 
				
			||||||
                #     traceback.print_exc()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        os.chdir(parent_path)
 | 
					        os.chdir(parent_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -82,49 +72,6 @@ class Plugins:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return module
 | 
					        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: []):
 | 
					    def execute_plugin(self, module: type, plugin: Plugin, loading_data: []):
 | 
				
			||||||
        plugin.reference = module.Plugin()
 | 
					        plugin.reference = module.Plugin()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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()
 | 
				
			||||||
@@ -7,12 +7,6 @@ from multiprocessing.connection import Listener, Client
 | 
				
			|||||||
# Application imports
 | 
					# Application imports
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def threaded(fn):
 | 
					 | 
				
			||||||
    def wrapper(*args, **kwargs):
 | 
					 | 
				
			||||||
        threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start()
 | 
					 | 
				
			||||||
    return wrapper
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class IPCServer:
 | 
					class IPCServer:
 | 
				
			||||||
@@ -36,7 +30,7 @@ class IPCServer:
 | 
				
			|||||||
            self._ipc_authkey = None
 | 
					            self._ipc_authkey = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @threaded
 | 
					    @daemon_threaded
 | 
				
			||||||
    def create_ipc_listener(self) -> None:
 | 
					    def create_ipc_listener(self) -> None:
 | 
				
			||||||
        if self._conn_type == "socket":
 | 
					        if self._conn_type == "socket":
 | 
				
			||||||
            if os.path.exists(self._ipc_address):
 | 
					            if os.path.exists(self._ipc_address):
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user