feat: add --dbus

This commit is contained in:
Johann Dreo 2025-08-09 21:35:17 +02:00
commit 43f7b2702c
2 changed files with 46 additions and 7 deletions

View file

@ -26,6 +26,7 @@ Currently, FlickSave can perform the following actions:
- `--inkscape`: export a timestamped PNG of a watched SVG, - `--inkscape`: export a timestamped PNG of a watched SVG,
- `--git`: commit the modifications of the watched file, - `--git`: commit the modifications of the watched file,
- `--log`: print a message stating that the watched file was touched. - `--log`: print a message stating that the watched file was touched.
- `--dbus`: send a notification to the system's D-Bus.
You can pass multiple actions. You can pass multiple actions.
For instance, you can both export a PNG and do a Git commit. For instance, you can both export a PNG and do a Git commit.
@ -36,7 +37,7 @@ For instance, with a target file named `test.svg`, a snapshot will look like `te
### Synopsis ### Synopsis
`flicksave.py [-h] [--save] [--inkscape] [--git] [--log] [-d DIRECTORY] [-y DELAY] [-s SEPARATOR] [-t TEMPLATE] [-w] [-v {DEBUG,INFO,WARNING,ERROR}] [-e {opened,moved,deleted,created,modified,closed}...] target` `flicksave.py [-h] [--save] [--inkscape] [--git] [--log] [--dbus] [-d DIRECTORY] [-y DELAY] [-s SEPARATOR] [-t TEMPLATE] [-w] [-v {DEBUG,INFO,WARNING,ERROR}] [-e {opened,moved,deleted,created,modified,closed}...] target`
### Required positional argument ### Required positional argument
@ -49,6 +50,7 @@ For instance, with a target file named `test.svg`, a snapshot will look like `te
* `--inkscape`: Save a PNG snpashot of the watched SVG file. * `--inkscape`: Save a PNG snpashot of the watched SVG file.
* `--git`: Commit the watched file if it has been modified. * `--git`: Commit the watched file if it has been modified.
* `--log`: Print a message when the watched file is touched. * `--log`: Print a message when the watched file is touched.
* `--dbus`: Send a notification to the system's D-Bus.
* `-d DIRECTORY`, `--directory DIRECTORY`: The directory in which to copy the saved versions. (default: .) * `-d DIRECTORY`, `--directory DIRECTORY`: The directory in which to copy the saved versions. (default: .)
* `-y DELAY`, `--delay DELAY`: The minimum time (in seconds) between the creation of different saved files. (default: 10) * `-y DELAY`, `--delay DELAY`: The minimum time (in seconds) between the creation of different saved files. (default: 10)
* `-s SEPARATOR`, `--separator SEPARATOR`: Separator character between the file name and the date stamp. (default: _) * `-s SEPARATOR`, `--separator SEPARATOR`: Separator character between the file name and the date stamp. (default: _)

View file

@ -9,6 +9,15 @@ import logging
import datetime import datetime
import subprocess import subprocess
try:
from sdbus_block.notifications import FreedesktopNotifications
except Exception as e:
logging.error(e)
logging.error("Suitable `sdbus` module cannot be loaded, the --dbus action is disabled.")
HAS_DBUS = False
else:
HAS_DBUS = True
from watchdog.observers import Observer from watchdog.observers import Observer
from watchdog.events import LoggingEventHandler from watchdog.events import LoggingEventHandler
from watchdog.events import FileSystemEventHandler from watchdog.events import FileSystemEventHandler
@ -27,6 +36,7 @@ class Flick:
def __repr__(self): def __repr__(self):
return f"Flick({self.date},{self.target},{self.ext})" return f"Flick({self.date},{self.target},{self.ext})"
class Flicker: class Flicker:
"""Build a new timestamped file name.""" """Build a new timestamped file name."""
def __init__(self, target, delay=10): def __init__(self, target, delay=10):
@ -55,7 +65,7 @@ class Flicker:
class Operator: class Operator:
"""Interface example for an Operator (but can actually be any callable with the same signature).""" """Interface example for an Operator (but can actually be any callable with the same signature)."""
def __call__(self, target, flick, alt_ext = None): def __call__(self, target, flick, event, alt_ext = None):
raise NotImplemented raise NotImplemented
@ -132,7 +142,7 @@ class Save(Operator):
self.last_date = self.find_last_save(self.flick.target) self.last_date = self.find_last_save(self.flick.target)
def __call__(self, target, flick, alt_ext = None): def __call__(self, target, flick, event, alt_ext = None):
self.last_date = self.find_last_save(self.flick.target) self.last_date = self.find_last_save(self.flick.target)
if self.last_date: if self.last_date:
logging.debug("Current date: %s", date_now.isoformat()) logging.debug("Current date: %s", date_now.isoformat())
@ -165,7 +175,7 @@ class Command(Save):
def __repr__(self): def __repr__(self):
return f"Command(\"{self.cmd}\",{self.save_dir}, {self.date_sep},{self.date_template},{self.alt_ext})" return f"Command(\"{self.cmd}\",{self.save_dir}, {self.date_sep},{self.date_template},{self.alt_ext})"
def __call__(self, target, flick): def __call__(self, target, flick, event):
# Change the extension, if asked. # Change the extension, if asked.
flickname,flickext = os.path.splitext(flick.target) flickname,flickext = os.path.splitext(flick.target)
if self.alt_ext: if self.alt_ext:
@ -191,8 +201,29 @@ class Log(Operator):
def __repr__(self): def __repr__(self):
return "Log()" return "Log()"
def __call__(self, target, flick, alt_ext = None): def __call__(self, target, flick, event, alt_ext = None):
logging.info("Event(s) seen for {}: {}".format(target,flick)) logging.info(f"File {target} was {event.event_type}")
if HAS_DBUS:
class DBus(Operator):
def __repr__(self):
return "Log()"
def __call__(self, target, flick, event, alt_ext = None):
logging.info("Event(s) seen for {}: {}".format(target,flick))
notif = FreedesktopNotifications()
hints = notif.create_hint(
urgency = 0,
category = "transfer", # See https://specifications.freedesktop.org/notification-spec/latest/categories.html
)
# app_name='', replaces_id=0, app_icon='', summary='', body='', actions=[], hints={}, expire_timeout=-1
notif.notify(
app_name = "FlickSave",
summary = f"File {target} was {event.event_type}",
body = str(flick),
hints = hints,
)
class Handler(FileSystemEventHandler): class Handler(FileSystemEventHandler):
@ -214,7 +245,7 @@ class Handler(FileSystemEventHandler):
for op in self.ops: for op in self.ops:
logging.debug("Calling %s", op) logging.debug("Calling %s", op)
op(os.path.abspath(event.src_path), flick) op(os.path.abspath(event.src_path), flick, event)
else: else:
logging.debug("Not handling event: file={}, is_directory={}, watched_types={}".format(os.path.abspath(event.src_path),event.is_directory, self.watched_types)) logging.debug("Not handling event: file={}, is_directory={}, watched_types={}".format(os.path.abspath(event.src_path),event.is_directory, self.watched_types))
@ -317,6 +348,10 @@ if __name__=="__main__":
["Log when the target file is modified.", ["Log when the target file is modified.",
None], None],
} }
if HAS_DBUS:
existing["dbus"] = \
["Send a notification (on the system's D-Bus).",
None]
def help(name): def help(name):
return existing[name][0] return existing[name][0]
@ -336,6 +371,8 @@ if __name__=="__main__":
available["inkscape"][1] = Command("inkscape {target} --without-gui --export-png={flick} --export-area-page", asked.directory, asked.separator, asked.template, "png", asked.no_overwrite) available["inkscape"][1] = Command("inkscape {target} --without-gui --export-png={flick} --export-area-page", asked.directory, asked.separator, asked.template, "png", asked.no_overwrite)
available["git"][1] = Command("git add {target} ; git commit -m 'Automatic flicksave commit: {flick}'") available["git"][1] = Command("git add {target} ; git commit -m 'Automatic flicksave commit: {flick}'")
available["log"][1] = Log() available["log"][1] = Log()
if HAS_DBUS:
available["dbus"][1] = DBus()
if __debug__: if __debug__:
# Check that both available and existing are aligned. # Check that both available and existing are aligned.