2019-12-10 21:02:04 +00:00
|
|
|
'''
|
2021-08-10 00:37:19 +00:00
|
|
|
Copy your file every few minutes while you work on it, so that you can have
|
|
|
|
snapshots of its history.
|
|
|
|
Not a replacement for real version control but could be applicable in very
|
|
|
|
simple situations or in cases where e.g. git is not.
|
2019-12-10 21:02:04 +00:00
|
|
|
'''
|
|
|
|
import argparse
|
|
|
|
import hashlib
|
|
|
|
import os
|
|
|
|
import shutil
|
|
|
|
import sys
|
|
|
|
import time
|
|
|
|
|
|
|
|
from voussoirkit import bytestring
|
|
|
|
from voussoirkit import pathclass
|
2020-01-16 06:47:26 +00:00
|
|
|
from voussoirkit import winglob
|
2019-12-10 21:02:04 +00:00
|
|
|
|
|
|
|
def hash_file(filepath, hasher):
|
|
|
|
bytestream = read_filebytes(filepath)
|
|
|
|
for chunk in bytestream:
|
|
|
|
hasher.update(chunk)
|
|
|
|
return hasher.hexdigest()
|
|
|
|
|
|
|
|
def hash_file_md5(filepath):
|
|
|
|
return hash_file(filepath, hasher=hashlib.md5())
|
|
|
|
|
|
|
|
def read_filebytes(filepath, chunk_size=bytestring.MIBIBYTE):
|
|
|
|
'''
|
|
|
|
Yield chunks of bytes from the file between the endpoints.
|
|
|
|
'''
|
|
|
|
filepath = pathclass.Path(filepath)
|
|
|
|
if not filepath.is_file:
|
|
|
|
raise FileNotFoundError(filepath)
|
|
|
|
|
2020-09-21 01:27:28 +00:00
|
|
|
f = filepath.open('rb')
|
2019-12-10 21:02:04 +00:00
|
|
|
with f:
|
|
|
|
while True:
|
|
|
|
chunk = f.read(chunk_size)
|
|
|
|
if len(chunk) == 0:
|
|
|
|
break
|
|
|
|
|
|
|
|
yield chunk
|
|
|
|
|
|
|
|
def filetimelapse(filepath, rate):
|
|
|
|
(noext, extension) = os.path.splitext(filepath)
|
|
|
|
|
|
|
|
last_hash = None
|
2020-01-16 06:47:26 +00:00
|
|
|
existing_timelapses = winglob.glob(f'{noext}-*.filetimelapse{extension}')
|
2019-12-10 21:02:04 +00:00
|
|
|
if existing_timelapses:
|
|
|
|
last_hash = hash_file_md5(existing_timelapses[-1])
|
|
|
|
print(f'Starting with previous {existing_timelapses[-1]} {last_hash}')
|
|
|
|
|
|
|
|
while True:
|
|
|
|
new_hash = hash_file_md5(filepath)
|
|
|
|
if new_hash != last_hash:
|
|
|
|
timestamp = time.strftime('%Y%m%d%H%M%S')
|
|
|
|
copy_name = f'{noext}-{timestamp}.filetimelapse{extension}'
|
|
|
|
shutil.copy(filepath, copy_name)
|
|
|
|
last_hash = new_hash
|
|
|
|
print(copy_name, new_hash)
|
|
|
|
time.sleep(rate)
|
|
|
|
|
|
|
|
def filetimelapse_argparse(args):
|
2020-06-04 03:35:07 +00:00
|
|
|
try:
|
|
|
|
return filetimelapse(args.filepath, args.rate)
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
pass
|
2019-12-10 21:02:04 +00:00
|
|
|
|
|
|
|
def main(argv):
|
|
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
|
|
|
|
|
|
parser.add_argument('filepath')
|
2021-02-21 05:01:55 +00:00
|
|
|
parser.add_argument('--rate', default=None, required=True, type=int)
|
2019-12-10 21:02:04 +00:00
|
|
|
parser.set_defaults(func=filetimelapse_argparse)
|
|
|
|
|
|
|
|
args = parser.parse_args(argv)
|
2020-02-09 01:18:50 +00:00
|
|
|
return args.func(args)
|
2019-12-10 21:02:04 +00:00
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
raise SystemExit(main(sys.argv[1:]))
|