diff options
| -rwxr-xr-x | pyLoadCli.py | 713 | ||||
| -rwxr-xr-x | pyLoadCore.py | 11 | 
2 files changed, 366 insertions, 358 deletions
| diff --git a/pyLoadCli.py b/pyLoadCli.py index 96165a7b5..d5e214ca4 100755 --- a/pyLoadCli.py +++ b/pyLoadCli.py @@ -1,274 +1,88 @@  #!/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 os.path +from os import chdir +from os.path import join +from os.path import abspath +from os.path import dirname +from os import sep +from time import sleep +import sys +import time +import threading +import xmlrpclib -""" -    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. +from module.XMLConfigParser import XMLConfigParser -    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. +class pyLoadCli: +    def __init__(self, server_url): +        self.core = xmlrpclib.ServerProxy(server_url, allow_none=True) +        self.getch = _Getch() +        self.input = "" +        self.pos = [0, 0] +        self.inputline = 0 +        self.menuline = 0 -    You should have received a copy of the GNU General Public License -    along with this program; if not, see <http://www.gnu.org/licenses/>. -     -    @author: mkaay -    @version: v0.3 -""" +        try: +            self.core.get_server_version() +        except: +            print "pyLoadCore not running" +            exit() -SERVER_VERSION = "0.3" +        self.links_added = 0 -import curses -from time import sleep, time -import xmlrpclib -from threading import RLock, Thread -import sys -from os import chdir -from os.path import dirname -from os.path import abspath -from os import sep -import ConfigParser +        os.system("clear") +        self.println(1, blue("py") + yellow("Load") + white(" Command Line Interface")) +        self.println(2, "") -class pyLoadCli: -    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.collectorbox = None -        self.add_win = None -        self.proxy = None -         -        self.downloads = [] -        self.tmp_bind = [] -        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) -        curses.init_pair(5, curses.COLOR_BLACK, curses.COLOR_WHITE        ) -         -        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("Status", "s", None) -        self.add_menu("Collector", "c", self.collector_menu) -        self.add_menu("Add-Link", "l", self.show_addl_box) -        self.add_menu("New-Package", "p", self.show_newp_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) -        server_version = self.proxy.get_server_version() -        self.lock.release() -        if not server_version == SERVER_VERSION: -            raise Exception("server is version %s client accepts version %s" % (server_version, SERVER_VERSION)) -     -    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, force=False): -        if self.current_dwin_rows != down_num or force: -            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_addl_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 = [] + +        self.file_list = {} + +        self.thread = RefreshThread(self) +        self.thread.start() + +        self.start() + +    def start(self):          while True: -            box.move(2, 2) -            s = box.getstr() -            if s == "END": -                break +            #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              else: -                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 show_newp_box(self): -        self.lock.acquire() -        curses.echo() -        box = self.screen.subwin(4, 75, 18, 2) -        box.box() -        self.lock.release() -        box.addstr(1, 2, "Package Name:") -        box.move(2, 2) -        s = box.getstr() -        box.erase() -        self.lock.acquire() -        curses.noecho() -        id = self.proxy.new_package(s) -        self.lock.release() -        self.show_package_edit(id) -     -    def show_package_edit(self, id): -        self.lock.acquire() -        self.tmp_bind = [] -        data = self.proxy.get_package_data(id) -        pfiles = self.proxy.get_package_files(id) -        box = self.screen.subwin(7+len(pfiles[0:5]), 71, 4, 4) -        box.box() -        box.bkgdset(" ", curses.color_pair(0)) -        self.lock.release() -        box.addstr(1, 2, "ID: %(id)s" % data) -        box.addstr(2, 2, "Name: %(package_name)s" % data) -        box.addstr(3, 2, "Folder: %(folder)s" % data) -        box.addstr(4, 2, "Files in Package:") -        for r, fid in enumerate(pfiles[0:5]): -            data = self.proxy.get_file_info(fid) -            box.addstr(5+r, 2, "#%(id)d - %(url)s" % data) -        box.move(len(pfiles[0:5])+5, 2) -        self.show_link_collector() -        curses.echo() -        fid = box.getstr() -        curses.noecho() -        self.proxy.move_file_2_package(int(fid), id) -        box.erase() -        self.hide_collector() -        self.redraw() -     -    def show_link_collector(self): -        self.lock.acquire() -        cfiles = self.proxy.get_collector_files() -        self.collectorbox = self.screen.subwin(len(cfiles[0:5])+2, 71, 14, 4) -        self.collectorbox.box() -        for r, fid in enumerate(cfiles[0:5]): -            data = self.proxy.get_file_info(fid) -            self.collectorbox.addstr(r+1, 2, "#%(id)d - %(url)s" % data) -        self.lock.release() -     -    def show_package_collector(self): -        show = True -        page = 0 -        rows_pp = 6 -        while show: -            self.lock.acquire() -            cpack = self.proxy.get_collector_packages() -            self.collectorbox = self.screen.subwin(2+len(cpack[rows_pp*page:rows_pp*(page+1)]), 71, 14, 4) -            self.collectorbox.box() -            for r, data in enumerate(cpack[rows_pp*page:rows_pp*(page+1)]): -                self.collectorbox.addstr(r+1, 2, "#%(id)d - %(package_name)s" % data) -            self.lock.release() -            self.refresh() -            c = self.collectorbox.getch() -            if c == ord("n"): -                if page <= float(len(cpack))/float(rows_pp)-1: -                    page = page+1 -            elif c == ord("p"): -                page = page-1 -                if page < 0: -                    page = 0 -            elif c == ord("d"): -                curses.echo() -                id = self.collectorbox.getstr() -                curses.noecho() -                self.proxy.push_package_2_queue(int(id)) -            else: -                show = False -            self.hide_collector() -     -    def hide_collector(self): -        self.lock.acquire() -        self.collectorbox.erase() -        self.lock.release() -         -    def collector_menu(self): -        menu = self.screen.subwin(4, 12, 2, 10) -        menu.box() -        menu.addstr(1, 1, "  inks    ") -        menu.addstr(2, 1, "  ackages ") -        menu.addstr(1, 2, "L", curses.A_BOLD | curses.A_UNDERLINE) -        menu.addstr(2, 2, "P", curses.A_BOLD | curses.A_UNDERLINE) -        c = menu.getch() -        menu.erase() -        self.redraw() -        if c == ord("l"): -            return -        elif c == ord("p"): -            self.show_package_collector() -     -    def update_status(self): -        self.update_downloads() -     +                self.input += inp +                self.print_input() +      def format_time(self, seconds):          seconds = int(seconds) @@ -277,86 +91,261 @@ class pyLoadCli:          return "%.2i:%.2i:%.2i" % (hours, minutes, seconds)      def format_size(self, size): -        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_tmp_func(self, key): -        for item in self.tmp_bind: -            if item[0] == key: -                return item[1] -        return None -     -    def get_command(self): -        c = self.screen.getch() -        if c == curses.KEY_END: -            self.exit() +        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 refresh(self): +        """Handle incoming data""" +        data = self.core.status_downloads() +            #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(data)))) +        line = 4 +        speed = 0 +        for download in 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 +        status = self.core.status_server() +        if status['pause']: +            self.println(line, "Status: " + red("paused") + " total Speed: " + red(str(int(speed)) + " kb/s") + " Files in queue: " + red(str(status["queue"])))          else: -            f = self.get_menu_func(c) -            if not f: -                f = self.get_tmp_func(c) -            if f: -                f() -        self.refresh() -     -    def redraw(self): -        self.adjust_download_win_size(len(self.downloads), force=True) -     -    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) +            self.println(line, "Status: " + red("running") + " total Speed: " + red(str(int(speed)) + " kb/s") + " Files in queue: " + red(str(status["queue"]))) +        line += 1 +        self.println(line, "") +        line += 1 +        self.menuline = line +         +        self.build_menu() +        # +        self.file_list = 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.core.toggle_pause() +            elif inp == "4": +                self.core.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 RefreshThread(threading.Thread): +    def __init__(self, cli): +        threading.Thread.__init__(self) +        self.cli = cli      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: +        while True: +            self.cli.refresh()              sleep(1) -    finally: -        getch_loop.stop() -        refresh_loop.stop() -        return +     + + + +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 '' +        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__": -if __name__=='__main__':      if len(sys.argv) > 1:          shortOptions = 'l' @@ -365,26 +354,44 @@ if __name__=='__main__':          opts, extraparams = __import__("getopt").getopt(sys.argv[1:], shortOptions, longOptions)          for option, params in opts:              if option in ("-l", "--local"): -                chdir(dirname(abspath(__file__)) + sep) -                config = ConfigParser.SafeConfigParser() -                config.read('config') -                ssl = "" -                if config.get("ssl", "activated") == "True": +                xmlconfig = XMLConfigParser(join(abspath(dirname(__file__)),"module","config","core.xml")) +                config = xmlconfig.getConfig() +                 +                ssl= "" +                if config['ssl']['activated'] == "True":                      ssl = "s" +                  server_url = "http%s://%s:%s@%s:%s/" % ( -                    ssl, -                    config.get("remote", "username"), -                    config.get("remote", "password"), -                    config.get("remote", "listenaddr"), -                    config.get("remote", "port") -                ) +                                        ssl, +                                        config['remote']['username'], +                                        config['remote']['password'], +                                        config['remote']['listenaddr'], +                                        config['remote']['port'] +                                        ) -        if len(extraparams) == 1: -            server_url = sys.argv[1] +        if len(extraparams) == 4: +            username, address, port, password = sys.argv[1:5] +             +            server_url = "http://%s:%s@%s:%s/" % ( +                                        username, +                                        adress, +                                        port, +                                        passwort +                                        ) +                  else: -        print "URL scheme: http[s]://user:password@host:port/" -        server_url = raw_input("URL: ") +        username = raw_input("Username:") +        address = raw_input("Adress:") +        port = raw_input("Port:") +        password = raw_input("Password:") +         +        server_url = "http://%s:%s@%s:%s/" % ( +                                        username, +                                        adress, +                                        port, +                                        passwort +                                        ) -    curses.wrapper(main) -    sys.exit() +     +    cli = pyLoadCli(server_url)
\ No newline at end of file diff --git a/pyLoadCore.py b/pyLoadCore.py index 1951d6db1..97ca25ffd 100755 --- a/pyLoadCore.py +++ b/pyLoadCore.py @@ -302,11 +302,6 @@ class Core(object):          elif start < now and end < now and start > end: return True
          else: return False
 -    #~ def init_webserver(self):
 -        #~ self.webserver = WebServer(self)
 -        #~ if self.config['webinterface']['activated']:
 -            #~ self.webserver.start()
 -            
      ####################################
      ########## XMLRPC Methods ##########
      ####################################
 @@ -371,6 +366,12 @@ class ServerMethods():      def unpause_server(self):
          self.core.thread_list.pause = False
 +    def toggle_pause(self):
 +        if self.core.thread_list.pause:
 +            self.core.thread_list.pause = False
 +        else:
 +            self.core.thread_list.pause = True
 +    
      def status_server(self):
          status = {}
          status['pause'] = self.core.thread_list.pause
 | 
