Files
flees/code/templates/client.py
2018-03-19 10:25:40 +02:00

302 lines
7.9 KiB
Python

#!/usr/bin/env python
import argparse, sys, os, subprocess, time, re, json
from subprocess import call, Popen, PIPE, STDOUT
import threading
import readline
from tabulate import tabulate
import glob
from io import StringIO
ROOTURL="{{ rooturl }}"
SHARE="{{ name }}"
TOKEN="{{ token }}"
class Completer(object):
def __init__(self, choices = []):
self.choices = choices
self.space = re.compile('.*\s+$', re.M)
def _listdir(self, path):
"List directory 'root' appending the path separator to subdirs."
files = []
for name in os.listdir(path):
file_path = os.path.join(path, name)
if os.path.isdir(file_path):
name += os.sep
files.append(name)
return files
def _complete_path(self, path=None):
"Perform completion of filesystem path."
if not path:
return self._listdir('.')
path = os.path.expanduser(path)
dirname, rest = os.path.split(path)
tmp = dirname if dirname else '.'
res = [os.path.join(dirname, p)
for p in self._listdir(tmp) if p.startswith(rest)]
# more than one match, or single match which does not exist (typo)
if len(res) > 1 or not os.path.exists(path):
return res
# resolved to a single directory, so return list of files below it
if os.path.isdir(path):
return [os.path.join(path, p) for p in self._listdir(path)]
# exact file match terminates this completion
return [path + ' ']
def complete(self, text, state):
"Generic readline completion entry point."
buffer = readline.get_line_buffer()
if len(self.choices) == 0:
return (self._complete_path(buffer) + [None])[state]
# show all commands
if len(buffer) == 0:
return [c + ' ' for c in self.choices][state]
# resolve command to the implementation function
cmd = buffer.strip()
results = [c + ' ' for c in self.choices if c.startswith(cmd)] + [None]
return results[state]
def download_file(file, opts):
print("Download "+file['name'])
cmd = [
'curl','--create-dirs',
'-o', file['name'],
'%s%s/%s/%s/%s'%(
opts.rooturl,
"download",
opts.share,
opts.token,
file['url']
),
]
p = Popen(
cmd,
stderr = PIPE,
)
for char in iter(lambda: p.stderr.read(1), ''):
if not char:
return
sys.stderr.write(char)
sys.stderr.flush()
return
def menu(opts):
commands = [
'Download',
'Upload'
]
print_title("Main")
for i,command in enumerate(commands):
print(" %d. %s"%( i+1, command, ))
comp = Completer(choices = ["1","2"])
readline.set_completer(comp.complete)
choice = user_input("Number of action: ").strip()
if choice == "1":
menu_download(opts)
if choice == "2":
menu_upload(opts)
if choice == "":
sys.exit(0)
def menu_download(opts):
while True:
print_title("Download files")
files = json.loads(run_command("file/details", opts))
file_table = []
for f in files:
file_table.append((f['name'], float(f['size'].replace(",","")), f['mtime']))
print(tabulate(file_table, headers = ("Name", "Size [Mb]", "Modified")))
name_list = [x['name'] for x in files]
comp = Completer(choices = name_list)
# we want to treat '/' as part of a word, so override the delimiters
readline.set_completer(comp.complete)
print("\n[Empty to return, * globs]")
choice = user_input('Download file: ').strip()
if choice == "":
return
if "*" in choice:
choice = choice.replace('*','.*')
choice_re = re.compile(choice)
choices = [i for i,x in enumerate(name_list) if choice_re.match(x)]
else:
if choice not in name_list:
print("No such file")
continue
choices = [ name_list.index(choice) ]
for choice in choices:
download_file(files[choice], opts)
def menu_upload(opts):
while True:
print_title("Upload")
comp = Completer(choices = [])
# we want to treat '/' as part of a word, so override the delimiters
readline.set_completer(comp.complete)
print("\n[Empty to return, * globs]")
choice = user_input('Upload file: ').strip()
if choice == "":
return
if "*" in choice:
choices = glob.glob(choice)
else:
if not os.path.exists(choice):
print("No such file")
continue
choices = [ choice ]
for choice in choices:
upload_file(choice, opts)
def parse_options():
parser = argparse.ArgumentParser(description='Flees client')
parser.add_argument('-s', action="store", type=int, dest="split", default = 32,
help = "Split size in megabytes [%(default)s]"
)
parser.add_argument('--rooturl', action="store", dest="rooturl",
default = ROOTURL,
help = "Address of Flees server [%(default)s]"
)
parser.add_argument('--share', action="store", dest="share",
default = SHARE,
help = "Name of Flees share [%(default)s]"
)
parser.add_argument('--token', action="store", dest="token",
default = TOKEN,
help = "API token for the share [%(default)s]"
)
return parser.parse_args()
def print_title(title):
print("%s\n%s\n"%(
title,
"="*len(title)
))
def read_output(stream):
for char in iter(lambda: stream.read(1), ''):
if not char:
break
sys.stderr.write(char)
sys.stderr.flush()
stream.close()
def run_command(command, opts):
p = Popen(
[
'curl','-s',
'%s%s/%s/%s'%(opts.rooturl, command, opts.share, opts.token)
],
stdout=PIPE,
stderr=PIPE
)
stdout_data, stderr_data = p.communicate()
return stdout_data
def run_loop(opts):
while True:
menu(opts)
def upload_file(file, opts):
print("Upload "+file)
p = Popen(
"curl -s %s%s/%s/%s | python -u - -s %d %s"%(
opts.rooturl,
"script/upload_split",
opts.share,
opts.token,
opts.split,
file
),
stdout = PIPE,
shell = True
)
read_output(p.stdout)
return
cmd1 = [
'curl','-s',
'%s%s/%s/%s'%(
opts.rooturl,
"script/upload_split",
opts.share,
opts.token,
),
]
cmd2 = [
'python', '-',
'-s', str(opts.split),
file
]
p1 = Popen(
cmd1,
stderr = PIPE,
stdout = PIPE,
)
script, stderr = p1.communicate()
p2 = Popen(
cmd2,
stdin = PIPE,
stdout = PIPE,
stderr = PIPE,
bufsize = 1
)
p2.stdin.write(script)
p2.stdin.close()
for char in iter(lambda: p2.stderr.read(1), ''):
if not char:
break
sys.stderr.write(char)
sys.stderr.flush()
#~ read_output(p2.stdout)
#~ tin = threading.Thread(
#~ target=write_input, args=(p2.stdin, script)
#~ )
#~ tout = threading.Thread(
#~ target=read_output, args=(p2.stdout,)
#~ )
#~ terr = threading.Thread(
#~ target=read_output, args=(p2.stderr,)
#~ )
#~ for t in (tin, tout, terr):
#~ t.daemon = True
#~ t.start()
#~ p2.wait()
return
def user_input(arg):
try:
return raw_input(arg)
except NameError:
return input(arg)
def write_input(stream, string):
stream.write(string)
stream.close()
if __name__ == "__main__":
readline.set_completer_delims(' \t\n;')
readline.parse_and_bind("tab: complete")
opts = parse_options()
run_loop(opts)