somewhat functioning flees client

This commit is contained in:
Ville Rantanen
2018-03-19 10:25:40 +02:00
parent acb366120d
commit 376869f1f3
3 changed files with 339 additions and 13 deletions

View File

@@ -215,6 +215,30 @@ def send(name):
return render_template('send.html',name=name) return render_template('send.html',name=name)
@app.route('/file/list/<name>/<token>', methods=['GET'])
def file_list(name, token):
(ok,share) = get_share(name, token = token)
if not ok:
return share
files = []
for file in iter_folder_files(share['path']):
files.append(path2url(file))
files.append("")
return "\n".join(files), 200
@app.route('/file/details/<name>/<token>', methods=['GET'])
def file_details(name, token):
(ok,share) = get_share(name, token = token)
if not ok:
return share
files = []
for file in iter_folder_files(share['path']):
status = file_stat(share['path'],file)
files.append(status)
return jsonify(files), 200
@app.route('/file/size/<name>/<token>/<path:filename>', methods=['GET']) @app.route('/file/size/<name>/<token>/<path:filename>', methods=['GET'])
def file_size(name, token, filename): def file_size(name, token, filename):
(ok,share) = get_share(name, token = token) (ok,share) = get_share(name, token = token)
@@ -230,18 +254,6 @@ def file_size(name, token, filename):
return str(size), 200 return str(size), 200
@app.route('/file/list/<name>/<token>', methods=['GET'])
def list_files(name, token):
(ok,share) = get_share(name, token = token)
if not ok:
return share
files = []
for file in iter_folder_files(share['path']):
files.append(path2url(file))
files.append("")
return "\n".join(files), 200
@app.route('/list/<name>/<token>', methods=['GET']) @app.route('/list/<name>/<token>', methods=['GET'])
@app.route('/list/<name>', methods=['GET']) @app.route('/list/<name>', methods=['GET'])
def list_view(name, token = None): def list_view(name, token = None):
@@ -350,6 +362,19 @@ def download_zip(name, token = None):
) )
@app.route('/script/client/<name>/<token>', methods=['GET'])
def script_client(name = None, token = None):
(ok,share) = get_share(name, token = token)
if not ok:
return share
return render_template(
"client.py",
name = name,
token = token,
rooturl = request.url_root
)
@app.route('/script/upload/<name>/<token>', methods=['GET']) @app.route('/script/upload/<name>/<token>', methods=['GET'])
def script_upload(name = None, token = None): def script_upload(name = None, token = None):
(ok,share) = get_share(name, token = token) (ok,share) = get_share(name, token = token)

View File

@@ -402,7 +402,7 @@ def print_rest_api_upload(config, share, token):
share['name'], share['name'],
token token
)) ))
print("\nLink to upload multiple files to the share, splitting large files (default 512Mb):") print("\nLink to upload multiple files to the share, splitting large files:")
print("\n# curl -s %s/script/upload_split/%s/%s | python - [-s split_size_in_Mb] file_to_upload.ext [second.file.ext]"%( print("\n# curl -s %s/script/upload_split/%s/%s | python - [-s split_size_in_Mb] file_to_upload.ext [second.file.ext]"%(
config['public_url'], config['public_url'],
share['name'], share['name'],

301
code/templates/client.py Normal file
View File

@@ -0,0 +1,301 @@
#!/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)