diff options
Diffstat (limited to 'pyload/cli')
| -rw-r--r-- | pyload/cli/AddPackage.py | 50 | ||||
| -rw-r--r-- | pyload/cli/Cli.py | 570 | ||||
| -rw-r--r-- | pyload/cli/Handler.py | 32 | ||||
| -rw-r--r-- | pyload/cli/ManageFiles.py | 178 | ||||
| -rw-r--r-- | pyload/cli/__init__.py | 4 | 
5 files changed, 834 insertions, 0 deletions
| diff --git a/pyload/cli/AddPackage.py b/pyload/cli/AddPackage.py new file mode 100644 index 000000000..f7e30b64e --- /dev/null +++ b/pyload/cli/AddPackage.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +# @author: RaNaN + +from pyload.cli.Handler import Handler +from pyload.utils.printer import * + + +class AddPackage(Handler): +    """ let the user add packages """ + +    def init(self): +        self.name = "" +        self.urls = [] + +    def onEnter(self, inp): +        if inp == "0": +            self.cli.reset() + +        if not self.name: +            self.name = inp +            self.setInput() +        elif inp == "END": +            #add package +            self.client.addPackage(self.name, self.urls, 1) +            self.cli.reset() +        else: +            if inp.strip(): +                self.urls.append(inp) +            self.setInput() + +    def renderBody(self, line): +        println(line, white(_("Add Package:"))) +        println(line + 1, "") +        line += 2 + +        if not self.name: +            println(line, _("Enter a name for the new package")) +            println(line + 1, "") +            line += 2 +        else: +            println(line, _("Package: %s") % self.name) +            println(line + 1, _("Parse the links you want to add.")) +            println(line + 2, _("Type %s when done.") % mag("END")) +            println(line + 3, _("Links added: ") + mag(len(self.urls))) +            line += 4 + +        println(line, "") +        println(line + 1, mag("0.") + _(" back to main menu")) + +        return line + 2 diff --git a/pyload/cli/Cli.py b/pyload/cli/Cli.py new file mode 100644 index 000000000..d8c8602fc --- /dev/null +++ b/pyload/cli/Cli.py @@ -0,0 +1,570 @@ +# -*- coding: utf-8 -*- +# @author: RaNaN + +from __future__ import with_statement + +from getopt import GetoptError, getopt + +import pyload.utils.pylgettext as gettext +import os +from os import _exit +from os.path import join, exists, abspath, basename +import sys +from sys import exit +from threading import Thread, Lock +from time import sleep +from traceback import print_exc + +from pyload.config.Parser import ConfigParser + +from codecs import getwriter + +if os.name == "nt": +    enc = "cp850" +else: +    enc = "utf8" + +sys.stdout = getwriter(enc)(sys.stdout, errors="replace") + +from pyload.cli.printer import * +from pyload.cli import AddPackage, ManageFiles + +from pyload.api import Destination +from pyload.utils import formatSize, decode +from pyload.remote.thriftbackend.ThriftClient import ThriftClient, NoConnection, NoSSL, WrongLogin, ConnectionClosed +from Getch import Getch +from rename_process import renameProcess + +class Cli(object): +    def __init__(self, client, command): +        self.client = client +        self.command = command + +        if not self.command: +            renameProcess('pyload-cli') +            self.getch = Getch() +            self.input = "" +            self.inputline = 0 +            self.lastLowestLine = 0 +            self.menuline = 0 + +            self.lock = Lock() + +            #processor funcions, these will be changed dynamically depending on control flow +            self.headerHandler = self   #the download status +            self.bodyHandler = self    #the menu section +            self.inputHandler = self + +            os.system("clear") +            println(1, blue("py") + yellow("Load") + white(_(" Command Line Interface"))) +            println(2, "") + +            self.thread = RefreshThread(self) +            self.thread.start() + +            self.start() +        else: +            self.processCommand() + +    def reset(self): +        """ reset to initial main menu """ +        self.input = "" +        self.headerHandler = self.bodyHandler = self.inputHandler = self + +    def start(self): +        """ main loop. handle input """ +        while True: +            #inp = raw_input() +            inp = self.getch.impl() +            if ord(inp) == 3: +                os.system("clear") +                sys.exit() # ctrl + c +            elif ord(inp) == 13: #enter +                try: +                    self.lock.acquire() +                    self.inputHandler.onEnter(self.input) + +                except Exception, e: +                    println(2, red(e)) +                finally: +                    self.lock.release() + +            elif ord(inp) == 127: +                self.input = self.input[:-1] #backspace +                try: +                    self.lock.acquire() +                    self.inputHandler.onBackSpace() +                finally: +                    self.lock.release() + +            elif ord(inp) == 27: #ugly symbol +                pass +            else: +                self.input += inp +                try: +                    self.lock.acquire() +                    self.inputHandler.onChar(inp) +                finally: +                    self.lock.release() + +            self.inputline = self.bodyHandler.renderBody(self.menuline) +            self.renderFooter(self.inputline) + + +    def refresh(self): +        """refresh screen""" + +        println(1, blue("py") + yellow("Load") + white(_(" Command Line Interface"))) +        println(2, "") + +        self.lock.acquire() + +        self.menuline = self.headerHandler.renderHeader(3) + 1 +        println(self.menuline - 1, "") +        self.inputline = self.bodyHandler.renderBody(self.menuline) +        self.renderFooter(self.inputline) + +        self.lock.release() + + +    def setInput(self, string=""): +        self.input = string + +    def setHandler(self, klass): +        #create new handler with reference to cli +        self.bodyHandler = self.inputHandler = klass(self) +        self.input = "" + +    def renderHeader(self, line): +        """ prints download status """ +        #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)))) + +        data = self.client.statusDownloads() +        speed = 0 + +        println(line, white(_("%s Downloads:") % (len(data)))) +        line += 1 + +        for download in data: +            if download.status == 12:  # downloading +                percent = download.percent +                z = percent / 4 +                speed += download.speed +                println(line, cyan(download.name)) +                line += 1 +                println(line, +                    blue("[") + yellow(z * "#" + (25 - z) * " ") + blue("] ") + green(str(percent) + "%") + _( +                        " Speed: ") + green(formatSize(download.speed) + "/s") + _(" Size: ") + green( +                        download.format_size) + _(" Finished in: ") + green(download.format_eta) + _( +                        " ID: ") + green(download.fid)) +                line += 1 +            if download.status == 5: +                println(line, cyan(download.name)) +                line += 1 +                println(line, _("waiting: ") + green(download.format_wait)) +                line += 1 + +        println(line, "") +        line += 1 +        status = self.client.statusServer() +        if status.pause: +            paused = _("Status:") + " " + red(_("paused")) +        else: +            paused = _("Status:") + " " + red(_("running")) + +        println(line,"%s %s: %s %s: %s %s: %s" % ( +            paused, _("total Speed"), red(formatSize(speed) + "/s"), _("Files in queue"), red( +                status.queue), _("Total"), red(status.total))) + +        return line + 1 + +    def renderBody(self, line): +        """ prints initial menu """ +        println(line, white(_("Menu:"))) +        println(line + 1, "") +        println(line + 2, mag("1.") + _(" Add Links")) +        println(line + 3, mag("2.") + _(" Manage Queue")) +        println(line + 4, mag("3.") + _(" Manage Collector")) +        println(line + 5, mag("4.") + _(" (Un)Pause Server")) +        println(line + 6, mag("5.") + _(" Kill Server")) +        println(line + 7, mag("6.") + _(" Quit")) + +        return line + 8 + +    def renderFooter(self, line): +        """ prints out the input line with input """ +        println(line, "") +        line += 1 + +        println(line, white(" Input: ") + decode(self.input)) + +        #clear old output +        if line < self.lastLowestLine: +            for i in range(line + 1, self.lastLowestLine + 1): +                println(i, "") + +        self.lastLowestLine = line + +        #set cursor to position +        print "\033[" + str(self.inputline) + ";0H" + +    def onChar(self, char): +        """ default no special handling for single chars """ +        if char == "1": +            self.setHandler(AddPackage) +        elif char == "2": +            self.setHandler(ManageFiles) +        elif char == "3": +            self.setHandler(ManageFiles) +            self.bodyHandler.target = Destination.Collector +        elif char == "4": +            self.client.togglePause() +            self.setInput() +        elif char == "5": +            self.client.kill() +            self.client.close() +            sys.exit() +        elif char == "6": +            os.system('clear') +            sys.exit() + +    def onEnter(self, inp): +        pass + +    def onBackSpace(self): +        pass + +    def processCommand(self): +        command = self.command[0] +        args = [] +        if len(self.command) > 1: +            args = self.command[1:] + +        if command == "status": +            files = self.client.statusDownloads() + +            if not files: +                print "No downloads running." + +            for download in files: +                if download.status == 12:  # downloading +                    print print_status(download) +                    print "\tDownloading: %s @ %s/s\t %s (%s%%)" % ( +                        download.format_eta, formatSize(download.speed), formatSize(download.size - download.bleft), +                        download.percent) +                elif download.status == 5: +                    print print_status(download) +                    print "\tWaiting: %s" % download.format_wait +                else: +                    print print_status(download) + +        elif command == "queue": +            print_packages(self.client.getQueueData()) + +        elif command == "collector": +            print_packages(self.client.getCollectorData()) + +        elif command == "add": +            if len(args) < 2: +                print _("Please use this syntax: add <Package name> <link> <link2> ...") +                return + +            self.client.addPackage(args[0], args[1:], Destination.Queue) + +        elif command == "add_coll": +            if len(args) < 2: +                print _("Please use this syntax: add <Package name> <link> <link2> ...") +                return + +            self.client.addPackage(args[0], args[1:], Destination.Collector) + +        elif command == "del_file": +            self.client.deleteFiles([int(x) for x in args]) +            print "Files deleted." + +        elif command == "del_package": +            self.client.deletePackages([int(x) for x in args]) +            print "Packages deleted." + +        elif command == "move": +            for pid in args: +                pack = self.client.getPackageInfo(int(pid)) +                self.client.movePackage((pack.dest + 1) % 2, pack.pid) + +        elif command == "check": +            print _("Checking %d links:") % len(args) +            print +            rid = self.client.checkOnlineStatus(args).rid +            self.printOnlineCheck(self.client, rid) + + +        elif command == "check_container": +            path = args[0] +            if not exists(join(owd, path)): +                print _("File does not exists.") +                return + +            f = open(join(owd, path), "rb") +            content = f.read() +            f.close() + +            rid = self.client.checkOnlineStatusContainer([], basename(f.name), content).rid +            self.printOnlineCheck(self.client, rid) + + +        elif command == "pause": +            self.client.pause() + +        elif command == "unpause": +            self.client.unpause() + +        elif command == "toggle": +            self.client.togglePause() + +        elif command == "kill": +            self.client.kill() +        elif command == "restart_file": +            for x in args: +                self.client.restartFile(int(x)) +            print "Files restarted." +        elif command == "restart_package": +            for pid in args: +                self.client.restartPackage(int(pid)) +            print "Packages restarted." + +        else: +            print_commands() + +    def printOnlineCheck(self, client, rid): +        while True: +            sleep(1) +            result = client.pollResults(rid) +            for url, status in result.data.iteritems(): +                if status.status == 2: check = "Online" +                elif status.status == 1: check = "Offline" +                else: check = "Unknown" + +                print "%-45s %-12s\t %-15s\t %s" % (status.name, formatSize(status.size), status.plugin, check) + +            if result.rid == -1: break + + +class RefreshThread(Thread): +    def __init__(self, cli): +        Thread.__init__(self) +        self.setDaemon(True) +        self.cli = cli + +    def run(self): +        while True: +            sleep(1) +            try: +                self.cli.refresh() +            except ConnectionClosed: +                os.system("clear") +                print _("pyLoad was terminated") +                _exit(0) +            except Exception, e: +                println(2, red(str(e))) +                self.cli.reset() +                print_exc() + + +def print_help(config): +    print +    print "pyLoad CLI Copyright (c) 2008-2015 the pyLoad Team" +    print +    print "Usage: [python] pyload-cli.py [options] [command]" +    print +    print "<Commands>" +    print "See pyload-cli.py -c for a complete listing." +    print +    print "<Options>" +    print "  -i, --interactive", " Start in interactive mode" +    print +    print "  -u, --username=", " " * 2, "Specify Username" +    print "  --pw=<password>", " " * 2, "Password" +    print "  -a, --address=", " " * 3, "Specify address (current=%s)" % config["addr"] +    print "  -p, --port", " " * 7, "Specify port (current=%s)" % config["port"] +    print +    print "  -l, --language", " " * 3, "Set user interface language (current=%s)" % config["language"] +    print "  -h, --help", " " * 7, "Display this help screen" +    print "  -c, --commands", " " * 3, "List all available commands" +    print + + +def print_packages(data): +    for pack in data: +        print "Package %s (#%s):" % (pack.name, pack.pid) +        for download in pack.links: +            print "\t" + print_file(download) +        print + + +def print_file(download): +    return "#%(id)-6d %(name)-30s %(statusmsg)-10s %(plugin)-8s" % { +        "id": download.fid, +        "name": download.name, +        "statusmsg": download.statusmsg, +        "plugin": download.plugin +    } + + +def print_status(download): +    return "#%(id)-6s %(name)-40s Status: %(statusmsg)-10s Size: %(size)s" % { +        "id": download.fid, +        "name": download.name, +        "statusmsg": download.statusmsg, +        "size": download.format_size +    } + + +def print_commands(): +    commands = [("status", _("Prints server status")), +        ("queue", _("Prints downloads in queue")), +        ("collector", _("Prints downloads in collector")), +        ("add <name> <link1> <link2>...", _("Adds package to queue")), +        ("add_coll <name> <link1> <link2>...", _("Adds package to collector")), +        ("del_file <fid> <fid2>...", _("Delete Files from Queue/Collector")), +        ("del_package <pid> <pid2>...", _("Delete Packages from Queue/Collector")), +        ("move <pid> <pid2>...", _("Move Packages from Queue to Collector or vice versa")), +        ("restart_file <fid> <fid2>...", _("Restart files")), +        ("restart_package <pid> <pid2>...", _("Restart packages")), +        ("check <container|url> ...", _("Check online status, works with local container")), +        ("check_container path", _("Checks online status of a container file")), +        ("pause", _("Pause the server")), +        ("unpause", _("continue downloads")), +        ("toggle", _("Toggle pause/unpause")), +        ("kill", _("kill server")), ] + +    print _("List of commands:") +    print +    for c in commands: +        print "%-35s %s" % c + + +def writeConfig(opts): +    try: +        with open(join(homedir, ".pyload-cli"), "w") as cfgfile: +            cfgfile.write("[cli]") +            for opt in opts: +                cfgfile.write("%s=%s\n" % (opt, opts[opt])) +    except Exception: +        print _("Couldn't write user config file") + + +def main(): +    config = {"addr": "127.0.0.1", "port": "7227", "language": "en"} +    try: +        config["language"] = os.environ["LANG"][0:2] +    except Exception: +        pass + +    if (not exists(join(pypath, "locale", config["language"]))) or config["language"] == "": +        config["language"] = "en" + +    configFile = ConfigParser.ConfigParser() +    configFile.read(join(homedir, ".pyload-cli")) + +    if configFile.has_section("cli"): +        for opt in configFile.items("cli"): +            config[opt[0]] = opt[1] + +    gettext.setpaths([join(os.sep, "usr", "share", "pyload", "locale"), None]) +    translation = gettext.translation("Cli", join(pypath, "locale"), +        languages=[config["language"], "en"], fallback=True) +    translation.install(unicode=True) + +    interactive = False +    command = None +    username = "" +    password = "" + +    shortOptions = 'iu:p:a:hcl:' +    longOptions = ['interactive', "username=", "pw=", "address=", "port=", "help", "commands", "language="] + +    try: +        opts, extraparams = getopt(sys.argv[1:], shortOptions, longOptions) +        for option, params in opts: +            if option in ("-i", "--interactive"): +                interactive = True +            elif option in ("-u", "--username"): +                username = params +            elif option in ("-a", "--address"): +                config["addr"] = params +            elif option in ("-p", "--port"): +                config["port"] = params +            elif option in ("-l", "--language"): +                config["language"] = params +                gettext.setpaths([join(os.sep, "usr", "share", "pyload", "locale"), None]) +                translation = gettext.translation("Cli", join(pypath, "locale"), +                    languages=[config["language"], "en"], fallback=True) +                translation.install(unicode=True) +            elif option in ("-h", "--help"): +                print_help(config) +                exit() +            elif option in ("--pw"): +                password = params +            elif option in ("-c", "--comands"): +                print_commands() +                exit() + +    except GetoptError: +        print 'Unknown Argument(s) "%s"' % " ".join(sys.argv[1:]) +        print_help(config) +        exit() + +    if len(extraparams) >= 1: +        command = extraparams + +    client = False + +    if interactive: +        try: +            client = ThriftClient(config["addr"], int(config["port"]), username, password) +        except WrongLogin: +            pass +        except NoSSL: +            print _("You need py-openssl to connect to this pyLoad Core.") +            exit() +        except NoConnection: +            config["addr"] = False +            config["port"] = False + +        if not client: +            if not config["addr"]: config["addr"] = raw_input(_("Address: ")) +            if not config["port"]: config["port"] = raw_input(_("Port: ")) +            if not username: username = raw_input(_("Username: ")) +            if not password: +                from getpass import getpass + +                password = getpass(_("Password: ")) + +            try: +                client = ThriftClient(config["addr"], int(config["port"]), username, password) +            except WrongLogin: +                print _("Login data is wrong.") +            except NoConnection: +                print _("Could not establish connection to %(addr)s:%(port)s." % {"addr": config["addr"], +                                                                                  "port": config["port"]}) + +    else: +        try: +            client = ThriftClient(config["addr"], int(config["port"]), username, password) +        except WrongLogin: +            print _("Login data is wrong.") +        except NoConnection: +            print _("Could not establish connection to %(addr)s:%(port)s." % {"addr": config["addr"], +                                                                              "port": config["port"]}) +        except NoSSL: +            print _("You need py-openssl to connect to this pyLoad core.") + +    if interactive and command: print _("Interactive mode ignored since you passed some commands.") + +    if client: +        writeConfig(config) +        cli = Cli(client, command) diff --git a/pyload/cli/Handler.py b/pyload/cli/Handler.py new file mode 100644 index 000000000..4aab3f0e2 --- /dev/null +++ b/pyload/cli/Handler.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# @author: RaNaN + +class Handler(object): +    def __init__(self, cli): +        self.cli = cli +        self.init() + +    client = property(lambda self: self.cli.client) +    input = property(lambda self: self.cli.input) + +    def init(self): +        pass + +    def onChar(self, char): +        pass + +    def onBackSpace(self): +        pass + +    def onEnter(self, inp): +        pass + +    def setInput(self, inp=""): +        self.cli.setInput(inp) + +    def backspace(self): +        self.cli.setInput(self.input[:-1]) + +    def renderBody(self, line): +        """ gets the line where to render output and should return the line number below its content """ +        return line + 1 diff --git a/pyload/cli/ManageFiles.py b/pyload/cli/ManageFiles.py new file mode 100644 index 000000000..ca1070113 --- /dev/null +++ b/pyload/cli/ManageFiles.py @@ -0,0 +1,178 @@ +# -*- coding: utf-8 -*- +# @author: RaNaN + +from itertools import islice +from time import time + +from pyload.cli.Handler import Handler +from pyload.utils.printer import * + +from pyload.api import Destination, PackageData + + +class ManageFiles(Handler): +    """ possibility to manage queue/collector """ + +    def init(self): +        self.target = Destination.Queue +        self.pos = 0  #position in queue +        self.package = -1  #choosen package +        self.mode = ""  # move/delete/restart +        self.cache = None +        self.links = None +        self.time = 0 + +    def onChar(self, char): +        if char in ("m", "d", "r"): +            self.mode = char +            self.setInput() +        elif char == "p": +            self.pos = max(0, self.pos - 5) +            self.backspace() +        elif char == "n": +            self.pos += 5 +            self.backspace() + +    def onBackSpace(self): +        if not self.input and self.mode: +            self.mode = "" +        if not self.input and self.package > -1: +            self.package = -1 + +    def onEnter(self, input): +        if input == "0": +            self.cli.reset() +        elif self.package < 0 and self.mode: +            #mode select +            packs = self.parseInput(input) +            if self.mode == "m": +                [self.client.movePackage((self.target + 1) % 2, x) for x in packs] +            elif self.mode == "d": +                self.client.deletePackages(packs) +            elif self.mode == "r": +                [self.client.restartPackage(x) for x in packs] + +        elif self.mode: +            #edit links +            links = self.parseInput(input, False) + +            if self.mode == "d": +                self.client.deleteFiles(links) +            elif self.mode == "r": +                map(self.client.restartFile, links) + +        else: +            #look into package +            try: +                self.package = int(input) +            except Exception: +                pass + +        self.cache = None +        self.links = None +        self.pos = 0 +        self.mode = "" +        self.setInput() + +    def renderBody(self, line): +        if self.package < 0: +            println(line, white(_("Manage Packages:"))) +        else: +            println(line, white((_("Manage Links:")))) +        line += 1 + +        if self.mode: +            if self.mode == "m": +                println(line, _("What do you want to move?")) +            elif self.mode == "d": +                println(line, _("What do you want to delete?")) +            elif self.mode == "r": +                println(line, _("What do you want to restart?")) + +            println(line + 1, "Enter single number, comma seperated numbers or ranges. eg. 1, 2, 3 or 1-3.") +            line += 2 +        else: +            println(line, _("Choose what yout want to do or enter package number.")) +            println(line + 1, ("%s - %%s, %s - %%s, %s - %%s" % (mag("d"), mag("m"), mag("r"))) % ( +                _("delete"), _("move"), _("restart"))) +            line += 2 + +        if self.package < 0: +            #print package info +            pack = self.getPackages() +            i = 0 +            for value in islice(pack, self.pos, self.pos + 5): +                try: +                    println(line, mag(str(value.pid)) + ": " + value.name) +                    line += 1 +                    i += 1 +                except Exception, e: +                    pass +            for x in range(5 - i): +                println(line, "") +                line += 1 +        else: +            #print links info +            pack = self.getLinks() +            i = 0 +            for value in islice(pack.links, self.pos, self.pos + 5): +                try: +                    println(line, mag(value.fid) + ": %s | %s | %s" % ( +                        value.name, value.statusmsg, value.plugin)) +                    line += 1 +                    i += 1 +                except Exception, e: +                    pass +            for x in range(5 - i): +                println(line, "") +                line += 1 + +        println(line, mag("p") + _(" - previous") + " | " + mag("n") + _(" - next")) +        println(line + 1, mag("0.") + _(" back to main menu")) +        return line + 2 + +    def getPackages(self): +        if self.cache and self.time + 2 < time(): +            return self.cache + +        if self.target == Destination.Queue: +            data = self.client.getQueue() +        else: +            data = self.client.getCollector() + +        self.cache = data +        self.time = time() + +        return data + +    def getLinks(self): +        if self.links and self.time + 1 < time(): +            return self.links +        try: +            data = self.client.getPackageData(self.package) +        except Exception: +            data = PackageData(links=[]) +        self.links = data +        self.time = time() +        return data + +    def parseInput(self, inp, package=True): +        inp = inp.strip() +        if "-" in inp: +            l, n, h = inp.partition("-") +            l = int(l) +            h = int(h) +            r = range(l, h + 1) + +            ret = [] +            if package: +                for p in self.cache: +                    if p.pid in r: +                        ret.append(p.pid) +            else: +                for l in self.links.links: +                    if l.lid in r: +                        ret.append(l.lid) +            return ret +        else: +            return [int(x) for x in inp.split(",")] diff --git a/pyload/cli/__init__.py b/pyload/cli/__init__.py new file mode 100644 index 000000000..a64fc0c0c --- /dev/null +++ b/pyload/cli/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +from pyload.cli.AddPackage import AddPackage +from pyload.cli.ManageFiles import ManageFiles | 
