diff --git a/webdav_upload/upload.py b/webdav_upload/upload.py index 96751acdf8e7e99dcc5fcb36530bc7b6f60673f0..b42dd30992ea073a634753e4094968abd30aedb4 100644 --- a/webdav_upload/upload.py +++ b/webdav_upload/upload.py @@ -1,8 +1,61 @@ import argparse import logging import os +import time from urllib.parse import urlparse -from webdav3.client import Client +from webdav3.client import Client, Urn, RemoteResourceNotFound + +TIME_FMT = '%a, %d %b %Y %H:%M:%S %Z' + + +def upload(client, local_path, remote_path, do_force, do_delete): + if os.path.isdir(local_path): + upload_directory(client, local_path, remote_path, do_force, do_delete) + else: + upload_file(client, local_path, remote_path, do_force) + + +def upload_directory(client, local_path, remote_path, do_force, do_delete): + client.mkdir(remote_path) + + remote_files = set(x.rstrip('/') for x in client.list(remote_path)) + + urn = Urn(remote_path, directory=True) + for resource_name in os.listdir(local_path): + _remote_path = "{parent}{name}".format(parent=urn.path(), name=resource_name).replace('\\', '') + _local_path = os.path.join(local_path, resource_name) + upload(client, _local_path, _remote_path, do_force, do_delete) + remote_files.discard(resource_name) + + if remote_files: + for f in remote_files: + _remote_path = "{parent}{name}".format(parent=urn.path(), name=f).replace('\\', '') + logging.info('deleting %s' % _remote_path) + client.clean(_remote_path) + + +def upload_file(client, local_path, remote_path, do_force): + if not do_force: + # Stat remote path and compare size and mtime. + local_stat = os.stat(local_path) + try: + remote_stat = client.info(remote_path) + local_mtime = time.mktime(time.gmtime(local_stat.st_mtime)) + remote_mtime = time.mktime(time.strptime(remote_stat['modified'], TIME_FMT)) + if int(remote_stat['size']) != local_stat.st_size: + logging.info('%s has changed (size differs)', remote_path) + elif remote_mtime <= local_mtime: + logging.info('%s has changed (local file is newer)', remote_path) + else: + logging.info('%s is unchanged, skipping...', remote_path) + return + except RemoteResourceNotFound: + pass + + logging.info('uploading %s to %s', local_path, remote_path) + urn = Urn(remote_path) + with open(local_path, 'rb') as fd: + client.execute_request(action='upload', path=urn.quote(), data=fd) def main(): @@ -24,7 +77,10 @@ def main(): 'like "https://www.autistici.org/dav/myuser/")') parser.add_argument( '--force', action='store_true', - help='always overwrite remote files') + help='always upload files even if they seem to not have changed') + parser.add_argument( + '--delete', action='store_true', + help='delete remote files that do not exist locally') parser.add_argument( '--verbose', action='store_true', help='enable WebDAV logging') @@ -39,7 +95,7 @@ def main(): parsed_url = urlparse(args.url) - logging.basicConfig(level=logging.DEBUG if args.verbose else logging.ERROR) + logging.basicConfig(level=logging.INFO if args.verbose else logging.ERROR) logging.getLogger('urllib3.connectionpool').setLevel(logging.ERROR) client = Client({ @@ -49,12 +105,8 @@ def main(): 'webdav_root': parsed_url.path, 'webdav_verbose': True, }) - if args.force: - client.upload_sync(args.dstdir, args.srcdir) - else: - client.push( - remote_directory=args.dstdir, - local_directory=args.srcdir) + + upload(client, args.srcdir, args.dstdir, args.force, args.delete) if __name__ == '__main__':