Updated READNE, added searcher plugin, cleanup
This commit is contained in:
parent
7abbee9182
commit
3f5664da5b
@ -11,10 +11,7 @@ sudo apt-get install python3.8 wget python3-setproctitle python3-gi ffmpegthumbn
|
||||
|
||||
# TODO
|
||||
<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 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 a basic favorites manager plugin.</li>
|
||||
</ul>
|
||||
|
3
plugins/file_properties/__init__.py
Normal file
3
plugins/file_properties/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Module
|
||||
"""
|
@ -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
|
||||
|
3
plugins/searcher/__init__.py
Normal file
3
plugins/searcher/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Module
|
||||
"""
|
281
plugins/searcher/plugin.py
Normal file
281
plugins/searcher/plugin.py
Normal file
@ -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:<Control>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"<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(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))
|
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
|
||||
"""
|
@ -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
|
||||
|
3
plugins/youtube_download/__init__.py
Normal file
3
plugins/youtube_download/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
"""
|
||||
Pligin Module
|
||||
"""
|
@ -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}"
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user