212 lines
8.3 KiB
Python
Executable file
212 lines
8.3 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
#encoding: utf-8
|
|
|
|
import sys
|
|
import argparse
|
|
import importlib.metadata
|
|
|
|
from . import *
|
|
|
|
def main():
|
|
class SortingHelpFormatter(argparse.HelpFormatter):
|
|
def add_arguments(self, actions):
|
|
actions = sorted(actions, key=attrgetter('option_strings'))
|
|
super(SortingHelpFormatter, self).add_arguments(actions)
|
|
|
|
class SaneHelpFormatter(
|
|
SortingHelpFormatter, argparse.RawTextHelpFormatter, argparse.ArgumentDefaultsHelpFormatter
|
|
):
|
|
pass
|
|
|
|
VERSION = importlib.metadata.version('watchyap')
|
|
|
|
# With default values in help message.
|
|
parser = argparse.ArgumentParser(formatter_class=SaneHelpFormatter,
|
|
add_help = False,
|
|
description="Perform an action each time a file is touched. For example: make a copy of a file each time it is modified.",
|
|
epilog=f"""Version: {VERSION}
|
|
|
|
Examples:
|
|
Copy the file each time it is modified:
|
|
watchyap --save my_file
|
|
Copy the file in a subdirectory each time it is modified:
|
|
watchyap --directory snapshots --save my_file
|
|
Export a PNG from a watched SVG, each time you hit 'save' in inkscape:
|
|
watchyap --inkscape my_file
|
|
Git commit the file if it has been modified, but not before 30 seconds after the previous commit:
|
|
watchyap --git --delay 30 my_file
|
|
Copy the file each time is is opened or closed:
|
|
watchyap --events opened closed --save my_file
|
|
You may want to save both the file before and after it was modified by any
|
|
program:
|
|
watchyap --save --events opened closed --no-overwrite my_file
|
|
""")
|
|
|
|
# Optional arguments.
|
|
parser.add_argument("-d","--directory", default='.',
|
|
help="The directory in which to copy the saved versions.")
|
|
|
|
parser.add_argument("-y","--delay", default=10, type=int,
|
|
help="The minimum time (in seconds) between the creation of different saved files.")
|
|
|
|
parser.add_argument("-e","--events", default="modified", nargs="*",
|
|
choices = ["opened", "moved", "deleted", "created", "modified", "closed"],
|
|
help="The event(s) on which you want to perform the action.")
|
|
|
|
parser.add_argument("-s","--separator", default='_',
|
|
help="Separator character between the file name and the date stamp.")
|
|
|
|
parser.add_argument("-t","--template", default='%Y-%m-%dT%H:%M:%S',
|
|
help="Template of the date stamp.")
|
|
|
|
parser.add_argument("-w", "--no-overwrite", action="store_true",
|
|
help="Do not overwrite snapshots created at the same time, but append a number to their name.")
|
|
|
|
parser.add_argument("-x", "--alt-ext", metavar="EXTENSION", default='',
|
|
help="Alternate extension for the timestamped snapshot (do not forget the dot).")
|
|
|
|
parser.add_argument("-v","--verbose", choices=['DEBUG','INFO','WARNING','ERROR'], default='INFO',
|
|
help="Verbosity level.")
|
|
log_as = { 'DEBUG' :logging.DEBUG,
|
|
'INFO' :logging.INFO,
|
|
'WARNING':logging.WARNING,
|
|
'ERROR' :logging.ERROR }
|
|
|
|
parser.add_argument("-V", "--version", action="store_true",
|
|
help="Show program's version number and exit.")
|
|
|
|
asked = parser.parse_known_args()[0]
|
|
|
|
if asked.version:
|
|
print(VERSION, file=sys.stderr)
|
|
sys.exit()
|
|
|
|
parser.add_argument("--cmd", metavar="COMMAND", type=str,
|
|
help="Execute the provided command as a action. You can use the following tags: {target} (watched file), {flick} (timestamped filename), {directory} (--directory where to put timestamped files), {separator} (--separator), {timestamp} (the timestamp formatted by --template), {alt_ext} (--alt-ext alternate extension), {no_overwrite} (--no-overwrite boolean, 1 if True, 0 if False), {event} (the event type).")
|
|
|
|
existing = {
|
|
"save":
|
|
["Save a snapshot of the target files.",
|
|
None],
|
|
"log":
|
|
["Log when the target files are touched.",
|
|
None],
|
|
}
|
|
if HAS_DBUS:
|
|
existing["dbus"] = \
|
|
["Send a notification (on the system's D-Bus).",
|
|
None]
|
|
|
|
if shutil.which("git"):
|
|
HAS_GIT = True
|
|
existing["git"] = ["Commit the target files if it has been modified [the repository should be set-up].", None]
|
|
existing["gitpush"] = ["Commit, pull and push the target files if it has been modified [the repository should be set-up].", None]
|
|
else:
|
|
HAS_GIT = False
|
|
|
|
if shutil.which("inkscape"):
|
|
HAS_INKSCAPE = True
|
|
existing["inkscape"] = ["Save a PNG snapshot of the files, using inkscape.", None]
|
|
else:
|
|
HAS_INKSCAPE = False
|
|
|
|
if shutil.which("zenity") and os.environ.get('DISPLAY'):
|
|
HAS_ZENITY = True
|
|
existing["zenity"] = ["Pop-up a dialog box each time a file is touched.", None]
|
|
else:
|
|
HAS_ZENITY = False
|
|
|
|
def help(name):
|
|
return existing[name][0]
|
|
|
|
for name in existing:
|
|
parser.add_argument("--"+name, help=help(name), action="store_true")
|
|
|
|
asked = parser.parse_known_args()[0]
|
|
|
|
logging.basicConfig(level=log_as[asked.verbose], format='%(asctime)s -- %(message)s', datefmt=asked.template)
|
|
|
|
logging.debug("Load actions...")
|
|
|
|
# Add instances, now that we have all parameters.
|
|
available = existing;
|
|
available["save"][1] = Save(asked.directory, asked.separator, asked.template, asked.no_overwrite)
|
|
available["log"][1] = Log()
|
|
available["cmd"] = ["", Command(asked.cmd, asked.directory, asked.separator, asked.template, asked.alt_ext, asked.no_overwrite)]
|
|
|
|
if HAS_GIT:
|
|
logging.debug("`git` command found")
|
|
available["git"][1] = Command("git add {target} ; git commit -m 'Automatic watchyap commit of {target} at {timestamp}'")
|
|
available["gitpush"][1] = Command("git add {target} ; git commit -m 'Automatic watchyap commit of {target} at {timestamp}' ; git pull ; git push")
|
|
else:
|
|
print("WARNING: `git` command not found, the --git* actions are disabled.", file=sys.stderr)
|
|
|
|
if HAS_INKSCAPE:
|
|
logging.debug("`inkscape` command found")
|
|
available["inkscape"][1] = Command("inkscape {target} --without-gui --export-png={flick} --export-area-page", asked.directory, asked.separator, asked.template, "png", asked.no_overwrite)
|
|
else:
|
|
print("WARNING: `inkscape` command not found, the --inkscape action is disabled.", file=sys.stderr)
|
|
|
|
if HAS_ZENITY:
|
|
logging.debug("`zenity` command found and display available")
|
|
available["zenity"][1] = Command("zenity --info --text 'File {target} was {event}'")
|
|
else:
|
|
has_zen = ""
|
|
if not shutil.which("zenity"):
|
|
has_zen = "`zenity` command not found, "
|
|
has_X = ""
|
|
if not os.environ.get('DISPLAY'):
|
|
has_X = "No graphical display found, "
|
|
print("WARNING: "+has_X + has_zen + "the --zenity action is disabled.", file=sys.stderr)
|
|
|
|
if HAS_DBUS:
|
|
available["dbus"][1] = DBus()
|
|
else:
|
|
print("WARNING: No suitable Python `sdbus` module found, the --dbus action is disabled.", file=sys.stderr)
|
|
|
|
# Check that both available and existing are aligned.
|
|
for op in existing:
|
|
assert op in available
|
|
for op in available:
|
|
assert op in existing
|
|
|
|
logging.debug("Available actions:")
|
|
for name in available:
|
|
logging.debug("\t%s",name)
|
|
|
|
# Required argument.
|
|
parser.add_argument("targets", nargs="+",
|
|
help="The files to save each time it's modified.")
|
|
|
|
parser.add_argument('-h', '--help', action='help', default='', help=('Show this help message and exit'))
|
|
|
|
asked = parser.parse_args()
|
|
|
|
actions = []
|
|
requested = vars(asked)
|
|
def instance(name):
|
|
return available[name][1]
|
|
for it in [iz for iz in requested if iz in available and requested[iz]]:
|
|
actions.append(instance(it))
|
|
|
|
if len(actions) == 0:
|
|
logging.warning(
|
|
"WARNING: you did not asked for any snapshot command, "
|
|
"I will only log when you save the file, but not perform any action. "
|
|
"Use one of the following option if you want me to do something: "
|
|
+", ".join(["--"+str(k) for k in available.keys()])
|
|
)
|
|
actions.append(Log())
|
|
|
|
logging.debug("Used actions:")
|
|
for op in actions:
|
|
logging.debug("\t%s", op)
|
|
|
|
# Start it.
|
|
logging.debug(asked.targets)
|
|
watchyap(asked.targets, actions, asked.delay, asked.events)
|
|
|
|
|
|
if __name__=="__main__":
|
|
main()
|
|
|