diff options
author | mkaay <mkaay@mkaay.de> | 2009-11-25 18:55:38 +0100 |
---|---|---|
committer | mkaay <mkaay@mkaay.de> | 2009-11-25 18:55:38 +0100 |
commit | 1d3b74dc93d403783924c654cc22f3304a1d7f0a (patch) | |
tree | 5e5ef2fed10c48dedf1445b70170a86cbf324e12 /pyLoadCli.py | |
parent | Webinterface with XMLRPC (diff) | |
download | pyload-1d3b74dc93d403783924c654cc22f3304a1d7f0a.tar.xz |
basic curses cli
Diffstat (limited to 'pyLoadCli.py')
-rwxr-xr-x | pyLoadCli.py | 527 |
1 files changed, 210 insertions, 317 deletions
diff --git a/pyLoadCli.py b/pyLoadCli.py index fd65e04da..186281e32 100755 --- a/pyLoadCli.py +++ b/pyLoadCli.py @@ -1,77 +1,142 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# -#Copyright (C) 2009 RaNaN -# -#This program is free software; you can redistribute it and/or modify -#it under the terms of the GNU General Public License as published by -#the Free Software Foundation; either version 3 of the License, -#or (at your option) any later version. -# -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -#See the GNU General Public License for more details. -# -#You should have received a copy of the GNU General Public License -# along with this program; if not, see <http://www.gnu.org/licenses/>. -# -### -import ConfigParser -import subprocess -import os +import curses, traceback, string, os +from time import sleep, time +import xmlrpclib +from threading import RLock, Thread +import sys import os.path from os import chdir from os.path import dirname from os.path import abspath from os import sep -from time import sleep -import sys -import time - -from module.remote.ClientSocket import SocketThread +import ConfigParser class pyLoadCli: - def __init__(self, adress, port, pw): - self.thread = SocketThread(adress, int(port), pw, self) - self.getch = _Getch() - self.input = "" - self.pos = [0, 0] - self.inputline = 0 - self.menuline = 0 - - self.links_added = 0 - - os.system("clear") - self.println(1, blue("py") + yellow("Load") + white(" Command Line Interface")) - self.println(2, "") - - - self.file_list = {} - self.thread.push_exec("get_links") - - self.start() - - def start(self): + menu_items = [] + + def __init__(self, stdscr, server_url): + self.stdscr = stdscr + self.lock = RLock() + self.lock.acquire() + self.stop = False + + self.download_win = None + self.add_win = None + self.proxy = None + + self.downloads = [] + self.current_dwin_rows = 0 + self.lock.release() + + self.connect(server_url) + + self.lock.acquire()
+ curses.init_pair(1, curses.COLOR_BLUE, curses.COLOR_BLACK)
+ curses.init_pair(2, curses.COLOR_YELLOW, curses.COLOR_BLACK)
+ curses.init_pair(3, curses.COLOR_GREEN, curses.COLOR_BLACK)
+ curses.init_pair(4, curses.COLOR_CYAN, curses.COLOR_BLACK) + + self.screen = self.stdscr.subwin(23, 79, 0, 0) + self.screen.box() + self.screen.addstr(1, 48, "py", curses.color_pair(1)) + self.screen.addstr(1, 50, "Load", curses.color_pair(2)) + self.screen.addstr(1, 55, "Command Line Interface") + self.lock.release() + + self.add_menu("Add", "a", self.show_add_box) + self.add_menu("Quit", "q", self.exit) + + self.init_download_win() + self.update_downloads() + + def connect(self, server_url): + self.lock.acquire() + self.proxy = xmlrpclib.ServerProxy(server_url, allow_none=True) + self.lock.release() + + def refresh(self): + self.lock.acquire()
+ self.screen.refresh() + self.lock.release() + + def init_download_win(self): + self.lock.acquire() + rows = 2 + if self.current_dwin_rows != 0: + rows = self.current_dwin_rows*3 + 1 + self.download_win = self.screen.subwin(rows, 75, 3, 2) + self.download_win.box() + self.lock.release() + + def adjust_download_win_size(self, down_num): + if self.current_dwin_rows != down_num: + self.lock.acquire() + self.download_win.erase() + self.current_dwin_rows = down_num + self.lock.release() + self.init_download_win() + self.screen.redrawwin() + + def update_downloads(self): + self.lock.acquire() + self.downloads = self.proxy.status_downloads() + self.lock.release() + self.adjust_download_win_size(len(self.downloads)) + self.show_downloads() + + def show_downloads(self): + self.lock.acquire() + self.download_win.redrawwin() + for r, d in enumerate(self.downloads): + r = r*3+1 + if d["status"] == "downloading": + self.download_win.addstr(r, 2, d["name"], curses.color_pair(4)) + self.download_win.addstr(r, 35, "[", curses.color_pair(1)) + self.download_win.addstr(r, 36, "#" * (int(d["percent"])/4), curses.color_pair(2)) + self.download_win.addstr(r, 61, "]", curses.color_pair(1)) + self.download_win.addstr(r, 63, "%s%%" % d["percent"], curses.color_pair(3)) + self.download_win.addstr(r+1, 8, "Speed:", curses.color_pair(0)) + self.download_win.addstr(r+1, 15, "%s kb/s" % int(d["speed"]), curses.color_pair(3)) + self.download_win.addstr(r+1, 25, "Size:", curses.color_pair(0)) + self.download_win.addstr(r+1, 31, self.format_size(d["size"]), curses.color_pair(3)) + self.download_win.addstr(r+1, 38, "ETA:", curses.color_pair(0)) + self.download_win.addstr(r+1, 43, self.format_time(d['eta']), curses.color_pair(3)) + self.download_win.addstr(r+1, 52, "ID:", curses.color_pair(0)) + self.download_win.addstr(r+1, 55, str(d["id"]), curses.color_pair(3)) + elif d["status"] == "waiting": + self.download_win.addstr(r, 2, d["name"], curses.color_pair(4)) + self.download_win.addstr(r+1, 4, "waiting: " + self.format_time(d["wait_until"]- time()), curses.color_pair(3)) + self.lock.release() + self.refresh() + + def show_add_box(self): + self.lock.acquire() + curses.echo() + box = self.screen.subwin(4, 75, 18, 2) + box.box() + self.lock.release() + box.addstr(1, 2, "URL: (type 'END' if done)") + rows = [] while True: - #inp = raw_input() - inp = self.getch.impl() - if ord(inp) == 3: - os.system("clear") - sys.exit() # ctrl + c - elif ord(inp) == 13: - self.handle_input() - self.input = "" #enter - self.print_input() - elif ord(inp) == 127: - self.input = self.input[:-1] #backspace - self.print_input() - elif ord(inp) == 27: #ugly symbol - pass + box.move(2, 2) + s = box.getstr() + if s == "END": + break else: - self.input += inp - self.print_input() - + rows.append(s) + box.addstr(2, 2, " "*72) + box.erase() + self.lock.acquire() + curses.noecho() + for row in rows: + if row[:7] == "http://" or self.proxy.file_exists(row): + self.proxy.add_urls([row]) + self.lock.release() + + def update_status(self): + self.update_downloads() + def format_time(self, seconds): seconds = int(seconds) @@ -80,247 +145,75 @@ class pyLoadCli: return "%.2i:%.2i:%.2i" % (hours, minutes, seconds) def format_size(self, size): - return str(size / 1024) + " MiB" - - def println(self, line, content): - print "\033[" + str(line) + ";0H\033[2K" + str(content) + "\033[" + str((self.inputline if self.inputline > 0 else self.inputline + 1) - 1) + ";0H" - - def print_input(self): - self.println(self.inputline, white(" Input: ") + self.input) - self.println(self.inputline + 1, "") - self.println(self.inputline + 2, "") - self.println(self.inputline + 3, "") - self.println(self.inputline + 4, "") - - def data_arrived(self, obj): - """Handle incoming data""" - if obj.command == "update": - #print updated information - print "\033[J" #clear screen - self.println(1, blue("py") + yellow("Load") + white(" Command Line Interface")) - self.println(2, "") - self.println(3, white("%s Downloads:" % (len(obj.data)))) - line = 4 - speed = 0 - for download in obj.data: - if download["status"] == "downloading": - percent = download["percent"] - z = percent / 4 - speed += download['speed'] - self.println(line, cyan(download["name"])) - line += 1 - self.println(line, blue("[") + yellow(z * "#" + (25-z) * " ") + blue("] ") + green(str(percent) + "%") + " Speed: " + green(str(int(download['speed'])) + " kb/s") + " Size: " + green(self.format_size(download['size'])) + " Finished in: " + green(self.format_time(download['eta'])) + " ID: " + green(str(download['id']))) - line += 1 - if download["status"] == "waiting": - self.println(line, cyan(download["name"])) - line += 1 - self.println(line, "waiting: " + green(self.format_time(download["wait_until"]- time.time()))) - line += 1 - self.println(line, "") - line += 1 - if obj.status['pause']: - self.println(line, "Status: " + red("paused") + " total Speed: " + red(str(int(speed)) + " kb/s") + " Files in queue: " + red(str(obj.status["queue"]))) - else: - self.println(line, "Status: " + red("running") + " total Speed: " + red(str(int(speed)) + " kb/s") + " Files in queue: " + red(str(obj.status["queue"]))) - line += 1 - self.println(line, "") - line += 1 - self.menuline = line - - self.build_menu() - elif obj.command == "file_list" or obj.function == "get_links": - self.file_list = obj.data - - def build_menu(self): - line = self.menuline - self.println(line, white("Menu:")) - line += 1 - if self.pos[0] == 0:# main menu - self.println(line, "") - line += 1 - self.println(line, mag("1.") + " Add Links") - line += 1 - self.println(line, mag("2.") + " Remove Links") - line += 1 - self.println(line, mag("3.") + " (Un)Pause Server") - line += 1 - self.println(line, mag("4.") + " Kill Server") - line += 1 - self.println(line, mag("5.") + " Quit") - line += 1 - self.println(line, "") - line += 1 - self.println(line, "") - elif self.pos[0] == 1:#add links - self.println(line, "Parse the links you want to add.") - line += 1 - self.println(line, "") - line += 1 - self.println(line, "") - line += 1 - self.println(line, "Links added: " + mag(str(self.links_added))) - line += 1 - self.println(line, "") - line += 1 - self.println(line, "") - line += 1 - self.println(line, mag("0.") + " back to main menu") - line += 1 - self.println(line, "") - elif self.pos[0] == 2:#remove links - self.println(line, "Type the number of the link you want to delete.") - line += 1 - i = 0 - for id in range(self.pos[1], self.pos[1] + 5): - if id < 0 or id >= len(self.file_list['order']): - continue - item = self.file_list['order'][id] - self.println(line, mag(str(item)) + ": " + self.file_list[item].url) - line += 1 - i += 1 - for x in range(5-i): - self.println(line, "") - line += 1 - - self.println(line, mag("p") + " - previous" + " | " + mag("n") + " - next") - line += 1 - self.println(line, mag("0.") + " back to main menu") - - self.inputline = line + 1 - self.print_input() - - def handle_input(self): - inp = self.input.strip() - if inp == "0": - self.pos = [0, 0] - self.build_menu() - return True - - if self.pos[0] == 0: - if inp == "1": - self.links_added = 0 - self.pos[0] = 1 - elif inp == "2": - self.pos[0] = 2 - self.pos[1] = 0 - elif inp == "3": - self.thread.push_exec("toggle_pause") - elif inp == "4": - self.thread.push_exec("kill") - sys.exit() - elif inp == "5": - os.system('clear') - sys.exit() - elif self.pos[0] == 1: #add links - if inp[:7] == "http://" or os.path.exists(inp): - self.thread.push_exec("add_links", [(inp, None)]) - self.links_added += 1 - elif self.pos[0] == 2: #remove links - if inp == "p": - self.pos[1] -= 5 - elif inp == "n": - self.pos[1] += 5 - else: - self.thread.push_exec("remove_links", [[inp]]) - - self.build_menu() - -class _Getch: - """ - Gets a single character from standard input. Does not echo to - the screen. - """ - def __init__(self): - try: - self.impl = _GetchWindows() - except ImportError: - try: - self.impl = _GetchMacCarbon() - except(AttributeError, ImportError): - self.impl = _GetchUnix() - - def __call__(self): return self.impl() - - -class _GetchUnix: - def __init__(self): - import tty - import sys - - def __call__(self): - import sys - import tty - import termios - - fd = sys.stdin.fileno() - old_settings = termios.tcgetattr(fd) - try: - tty.setraw(sys.stdin.fileno()) - ch = sys.stdin.read(1) - finally: - termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) - return ch - - -class _GetchWindows: - def __init__(self): - import msvcrt - - def __call__(self): - import msvcrt - return msvcrt.getch() - -class _GetchMacCarbon: - """ - A function which returns the current ASCII key that is down; - if no ASCII key is down, the null string is returned. The - page http://www.mactech.com/macintosh-c/chap02-1.html was - very helpful in figuring out how to do this. - """ - def __init__(self): - import Carbon - Carbon.Evt #see if it has this (in Unix, it doesn't) - - def __call__(self): - import Carbon - if Carbon.Evt.EventAvail(0x0008)[0] == 0: # 0x0008 is the keyDownMask - return '' + return str(size / 1024) + " MB" + + def add_menu(self, name, key, func): + self.lock.acquire() + left = 2 + for item in self.menu_items: + left += len(item[0]) + 1 + self.menu_items.append((name, key.lower(), func)) + self.screen.addstr(1, left, name) + p = name.lower().find(key.lower()) + if not p == -1: + self.screen.addstr(1, left+p, name[p], curses.A_BOLD | curses.A_UNDERLINE) + self.lock.release() + + def get_menu_func(self, key): + for item in self.menu_items: + if ord(item[1]) == key: + return item[2] + return None + + def get_command(self): + c = self.screen.getch() + if c == curses.KEY_END: + self.exit() else: - # - # The event contains the following info: - # (what,msg,when,where,mod)=Carbon.Evt.GetNextEvent(0x0008)[1] - # - # The message (msg) contains the ASCII char which is - # extracted with the 0x000000FF charCodeMask; this - # number is converted to an ASCII character with chr() and - # returned - # - (what, msg, when, where, mod) = Carbon.Evt.GetNextEvent(0x0008)[1] - return chr(msg) - -def blue(string): - return "\033[1;34m" + string + "\033[0m" - -def green(string): - return "\033[1;32m" + string + "\033[0m" - -def yellow(string): - return "\033[1;33m" + string + "\033[0m" - -def red(string): - return "\033[1;31m" + string + "\033[0m" - -def cyan(string): - return "\033[1;36m" + string + "\033[0m" - -def mag(string): - return "\033[1;35m" + string + "\033[0m" - -def white(string): - return "\033[1;37m" + string + "\033[0m" - -if __name__ == "__main__": - + f = self.get_menu_func(c) + if f: + f() + self.refresh() + + def exit(self): + self.stop = True + +class LoopThread(Thread): + def __init__(self, func, ret_func=None, sleep_time=None): + self.func = func + self.ret_func = ret_func + self.sleep_time = sleep_time + self.running = True + Thread.__init__(self) + + def run(self): + while self.running: + if self.sleep_time: + sleep(self.sleep_time) + ret = self.func() + if self.ret_func: + self.ret_func(ret) + + def stop(self): + self.running = False + +server_url = "" +def main(stdscr): + global server_url
+ cli = pyLoadCli(stdscr, server_url) + refresh_loop = LoopThread(cli.update_status, sleep_time=1) + refresh_loop.start() + getch_loop = LoopThread(cli.get_command) + getch_loop.start() + try: + while not cli.stop: + sleep(1) + finally: + getch_loop.stop() + refresh_loop.stop() + return + +if __name__=='__main__': if len(sys.argv) > 1: shortOptions = 'l' @@ -332,19 +225,19 @@ if __name__ == "__main__": chdir(dirname(abspath(__file__)) + sep) config = ConfigParser.SafeConfigParser() config.read('config') - - pipe = subprocess.PIPE - subprocess.Popen("./pyLoadCore.py", stdout=pipe, stderr=pipe) - print "Starting pyLoad Core" - sleep(1) - cli = pyLoadCli("127.0.0.1", config.get("remote", "port"), config.get("remote", "password")) - if len(extraparams) == 3: - address, port, password = sys.argv[1:4] - cli = pyLoadCli(address, port, password) + server_url = "https://%s:%s@%s:%s/" % ( + config.get("remote", "username"), + config.get("remote", "password"), + config.get("remote", "listenaddr"), + config.get("remote", "port") + ) + + if len(extraparams) == 1: + server_url = sys.argv[1] else: - address = raw_input("Adress:") - port = raw_input("Port:") - password = raw_input("Password:") - cli = pyLoadCli(address, port, password) - + print "URL scheme: https://user:password@host:port/" + server_url = raw_input("URL: ") + + curses.wrapper(main) + sys.exit() |