diff --git a/README.md b/README.md index c61ddf6..f405c7f 100644 --- a/README.md +++ b/README.md @@ -11,10 +11,7 @@ sudo apt-get install python3.8 wget python3-setproctitle python3-gi ffmpegthumbn # TODO diff --git a/plugins/file_properties/__init__.py b/plugins/file_properties/__init__.py new file mode 100644 index 0000000..d36fa8c --- /dev/null +++ b/plugins/file_properties/__init__.py @@ -0,0 +1,3 @@ +""" + Pligin Module +""" diff --git a/plugins/file_properties/plugin.py b/plugins/file_properties/plugin.py index 9236370..5a4b2a4 100644 --- a/plugins/file_properties/plugin.py +++ b/plugins/file_properties/plugin.py @@ -2,7 +2,7 @@ import os, threading, subprocess, time, pwd, grp from datetime import datetime -# Gtk imports +# Lib imports import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk, GLib, Gio diff --git a/plugins/searcher/__init__.py b/plugins/searcher/__init__.py new file mode 100644 index 0000000..d36fa8c --- /dev/null +++ b/plugins/searcher/__init__.py @@ -0,0 +1,3 @@ +""" + Pligin Module +""" diff --git a/plugins/searcher/plugin.py b/plugins/searcher/plugin.py new file mode 100644 index 0000000..ea295eb --- /dev/null +++ b/plugins/searcher/plugin.py @@ -0,0 +1,281 @@ +# Python imports +import os, multiprocessing, threading, subprocess, inspect, time, json +from multiprocessing import Manager, Process + +# Lib imports +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, GLib, GObject + +# Application imports + + +# NOTE: Threads WILL NOT die with parent's destruction. +def threaded(fn): + def wrapper(*args, **kwargs): + threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=False).start() + return wrapper + +# NOTE: Threads WILL die with parent's destruction. +def daemon_threaded(fn): + def wrapper(*args, **kwargs): + threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start() + return wrapper + + + + +class Manifest: + path: str = os.path.dirname(os.path.realpath(__file__)) + name: str = "Search" + author: str = "ITDominator" + version: str = "0.0.1" + support: str = "" + requests: {} = { + 'ui_target': "context_menu", + 'pass_fm_events': "true", + 'bind_keys': [f"{name}||_show_grep_list_page:f"] + } + + + + +class FilePreviewWidget(Gtk.LinkButton): + def __init__(self, path, file): + super(FilePreviewWidget, self).__init__() + self.set_label(file) + self.set_uri(f"file://{path}") + self.show_all() + + +class GrepPreviewWidget(Gtk.Box): + def __init__(self, path, sub_keys, data): + super(GrepPreviewWidget, self).__init__() + self.set_orientation(Gtk.Orientation.VERTICAL) + self.line_color = "#e0cc64" + + + _label = '/'.join( path.split("/")[-3:] ) + title = Gtk.LinkButton.new_with_label(uri=f"file://{path}", label=_label) + + self.add(title) + for key in sub_keys: + line_num = key + text = data[key] + box = Gtk.Box() + number_label = Gtk.Label() + text_view = Gtk.Label(label=text[:-1]) + label_text = f"{line_num}" + + number_label.set_markup(label_text) + number_label.set_margin_left(15) + number_label.set_margin_right(5) + number_label.set_margin_top(5) + number_label.set_margin_bottom(5) + text_view.set_margin_top(5) + text_view.set_margin_bottom(5) + text_view.set_line_wrap(True) + + box.add(number_label) + box.add(text_view) + self.add(box) + + self.show_all() + + + +manager = Manager() +grep_result_set = manager.dict() +file_result_set = manager.list() + + +class Plugin(Manifest): + def __init__(self): + self._GLADE_FILE = f"{self.path}/search_dialog.glade" + self._builder = None + self._search_dialog = None + + self._event_system = None + self._event_sleep_time = .5 + self._event_message = None + + self._active_path = None + self._file_list = None + self._grep_list = None + self._grep_proc = None + self._list_proc = None + + + def get_ui_element(self): + self._builder = Gtk.Builder() + self._builder.add_from_file(self._GLADE_FILE) + + classes = [self] + handlers = {} + for c in classes: + methods = None + try: + methods = inspect.getmembers(c, predicate=inspect.ismethod) + handlers.update(methods) + except Exception as e: + print(repr(e)) + + self._builder.connect_signals(handlers) + + self._search_dialog = self._builder.get_object("search_dialog") + self._grep_list = self._builder.get_object("grep_list") + self._file_list = self._builder.get_object("file_list") + + GObject.signal_new("update-file-ui-signal", self._search_dialog, GObject.SIGNAL_RUN_LAST, GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,)) + self._search_dialog.connect("update-file-ui-signal", self._load_file_ui) + GObject.signal_new("update-grep-ui-signal", self._search_dialog, GObject.SIGNAL_RUN_LAST, GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,)) + self._search_dialog.connect("update-grep-ui-signal", self._load_grep_ui) + + button = Gtk.Button(label=self.name) + button.connect("button-release-event", self._show_grep_list_page) + return button + + def set_fm_event_system(self, fm_event_system): + self._event_system = fm_event_system + + def run(self): + self._module_event_observer() + + + @daemon_threaded + def _show_grep_list_page(self, widget=None, eve=None): + self._event_system.push_gui_event([self.name, "get_current_state", ()]) + self.wait_for_fm_message() + + state = self._event_message + self._event_message = None + + GLib.idle_add(self._process_queries, (state)) + + def _process_queries(self, state): + self._active_path = state.tab.get_current_directory() + response = self._search_dialog.run() + self._search_dialog.hide() + + + def _run_find_file_query(self, widget=None, eve=None): + if self._list_proc: + self._list_proc.terminate() + self._list_proc = None + time.sleep(.2) + + del file_result_set[:] + self.clear_children(self._file_list) + + query = widget.get_text() + if query: + self._list_proc = multiprocessing.Process(self._do_list_search(self._active_path, query)) + self._list_proc.start() + + def _do_list_search(self, path, query): + self._file_traverse_path(path, query) + for target, file in file_result_set: + widget = FilePreviewWidget(target, file) + self._search_dialog.emit("update-file-ui-signal", (widget)) + + def _load_file_ui(self, parent=None, widget=None): + self._file_list.add(widget) + + def _file_traverse_path(self, path, query): + try: + for file in os.listdir(path): + target = os.path.join(path, file) + if os.path.isdir(target): + self._file_traverse_path(target, query) + else: + if query.lower() in file.lower(): + file_result_set.append([target, file]) + except Exception as e: + if debug: + print("Couldn't traverse to path. Might be permissions related...") + + + def _run_grep_query(self, widget=None, eve=None): + if self._grep_proc: + self._grep_proc.terminate() + self._grep_proc = None + time.sleep(.2) + + grep_result_set.clear() + self.clear_children(self._grep_list) + + query = widget.get_text() + if query: + self._grep_proc = multiprocessing.Process(self._do_grep_search(self._active_path, query)) + self._grep_proc.start() + + def _do_grep_search(self, path, query): + self._grep_traverse_path(path, query) + + keys = grep_result_set.keys() + for key in keys: + sub_keys = grep_result_set[key].keys() + widget = GrepPreviewWidget(key, sub_keys, grep_result_set[key]) + self._search_dialog.emit("update-grep-ui-signal", (widget)) + + def _load_grep_ui(self, parent=None, widget=None): + self._grep_list.add(widget) + + def _grep_traverse_path(self, path, query): + try: + for file in os.listdir(path): + target = os.path.join(path, file) + if os.path.isdir(target): + self._grep_traverse_path(target, query) + else: + self._search_for_string(target, query) + except Exception as e: + if debug: + print("Couldn't traverse to path. Might be permissions related...") + + def _search_for_string(self, file, query): + try: + with open(file, 'r') as fp: + for i, line in enumerate(fp): + if query in line: + if f"{file}" in grep_result_set.keys(): + grep_result_set[f"{file}"][f"{i+1}"] = line + else: + grep_result_set[f"{file}"] = {} + grep_result_set[f"{file}"] = {f"{i+1}": line} + except Exception as e: + if debug: + print("Couldn't read file. Might be binary or other cause...") + + + + + def clear_children(self, widget: type) -> None: + ''' Clear children of a gtk widget. ''' + for child in widget.get_children(): + widget.remove(child) + + def wait_for_fm_message(self): + while not self._event_message: + pass + + @daemon_threaded + def _module_event_observer(self): + while True: + time.sleep(self._event_sleep_time) + event = self._event_system.read_module_event() + if event: + try: + if event[0] == self.name: + target_id, method_target, data = self._event_system.consume_module_event() + + if not method_target: + self._event_message = data + else: + method = getattr(self.__class__, f"{method_target}") + if data: + data = method(*(self, *data)) + else: + method(*(self,)) + except Exception as e: + print(repr(e)) diff --git a/plugins/searcher/search_dialog.glade b/plugins/searcher/search_dialog.glade new file mode 100644 index 0000000..47fc1bc --- /dev/null +++ b/plugins/searcher/search_dialog.glade @@ -0,0 +1,227 @@ + + + + + + False + 6 + Search... + True + center-on-parent + 720 + 620 + True + dialog + True + True + center + + + True + False + 12 + + + True + False + end + + + gtk-cancel + True + True + True + False + True + + + True + True + 0 + + + + + gtk-ok + True + True + True + False + True + + + True + True + 1 + + + + + False + False + end + 0 + + + + + True + True + False + + + True + False + vertical + + + True + True + Query... + edit-find-symbolic + False + False + Search for file... + + + + False + True + 0 + + + + + True + True + True + True + in + + + True + False + + + True + False + vertical + 5 + top + + + + + + + + + + True + True + 1 + + + + + + + True + False + File Search + + + False + + + + + True + False + vertical + + + True + True + Query... + edit-find-symbolic + False + False + Query string in file... + + + + False + True + 0 + + + + + True + True + True + True + in + + + True + False + + + True + False + vertical + 5 + top + + + + + + + + + + True + True + 1 + + + + + 1 + + + + + True + False + Grep Search + + + 1 + False + + + + + + + + + + + True + True + 1 + + + + + + cancel_button + ok_button + + + diff --git a/plugins/template/__init__.py b/plugins/template/__init__.py new file mode 100644 index 0000000..d36fa8c --- /dev/null +++ b/plugins/template/__init__.py @@ -0,0 +1,3 @@ +""" + Pligin Module +""" diff --git a/plugins/template/plugin.py b/plugins/template/plugin.py index 294aadc..bc22725 100644 --- a/plugins/template/plugin.py +++ b/plugins/template/plugin.py @@ -1,7 +1,7 @@ # Python imports import os, threading, subprocess, time -# Gtk imports +# Lib imports import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk diff --git a/plugins/youtube_download/__init__.py b/plugins/youtube_download/__init__.py new file mode 100644 index 0000000..d36fa8c --- /dev/null +++ b/plugins/youtube_download/__init__.py @@ -0,0 +1,3 @@ +""" + Pligin Module +""" diff --git a/plugins/youtube_download/download.sh b/plugins/youtube_download/download.sh index dc6df21..073b354 100755 --- a/plugins/youtube_download/download.sh +++ b/plugins/youtube_download/download.sh @@ -10,7 +10,6 @@ function main() { cd "$(dirname "")" echo "Working Dir: " $(pwd) - source "/home/abaddon/Portable_Apps/py-venvs/yt-dlp-venv/venv/bin/activate" LINK=`xclip -selection clipboard -o` yt-dlp --write-sub --embed-sub --sub-langs en -o "${1}/%(title)s.%(ext)s" "${LINK}" diff --git a/plugins/youtube_download/plugin.py b/plugins/youtube_download/plugin.py index a7a6c44..cd98a37 100644 --- a/plugins/youtube_download/plugin.py +++ b/plugins/youtube_download/plugin.py @@ -1,7 +1,7 @@ # Python imports import os, threading, subprocess, time -# Gtk imports +# Lib imports import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk