#!/usr/bin/env python import argparse, sys, os, subprocess, time, re, json from subprocess import call, Popen, PIPE, STDOUT import threading import readline import glob from io import StringIO try: from tabulate import tabulate except ImportError: print("Install tabulate with: pip install --user tabulate") def tabulate(table, headers): value = [] value.append("\t".join(headers)) for row in table: value.append("\t".join([str(c) for c in row])) value.append("\n\nGet nicer tables by installing 'tabulate' package!\n") return "\n".join(value) 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") files = sorted(os.listdir(".")) file_table = [] for f in files: if f.startswith("."): continue if os.path.isfile(f): file_size = round(float(os.path.getsize(f)) / 1024 / 1024, 2) else: file_size = "[DIR]" f += "/" file_table.append((f, file_size)) print(tabulate(file_table, headers = ("Name", "Size [Mb]"))) 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): try: while True: menu(opts) except KeyboardInterrupt: print("") return 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)