#!/usr/bin/env python # -*- coding: utf-8 -*- # #Copyright (C) 2011 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 . # ### from getopt import GetoptError, getopt import gettext from itertools import islice import os from os import _exit from os.path import join import sys from sys import exit import threading from time import sleep from traceback import print_exc from codecs import getwriter sys.stdout = getwriter("utf8")(sys.stdout, errors="replace") from module import InitHomeDir from module.utils import formatSize, decode from module.ConfigParser import ConfigParser from module.remote.thriftbackend.ThriftClient import ThriftClient, NoConnection, NoSSL, WrongLogin, PackageDoesNotExists, ConnectionClosed class Cli: def __init__(self, client, command): self.client = client self.command = command if not self.command: self.getch = _Getch() self.input = "" self.pos = [0, 0, 0] self.inputline = 0 self.menuline = 0 self.new_package = {} 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 = RefreshThread(self) self.thread.start() self.start() else: self.processCommand() def start(self): while True: #inp = raw_input() inp = self.getch.impl() if ord(inp) == 3: os.system("clear") sys.exit() # ctrl + c elif ord(inp) == 13: try: self.handle_input() except Exception, e: self.println(2, red(e)) 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: self.input += inp self.print_input() def format_time(self, seconds): seconds = int(seconds) hours, seconds = divmod(seconds, 3600) minutes, seconds = divmod(seconds, 60) return "%.2i:%.2i:%.2i" % (hours, minutes, seconds) def println(self, line, content): print "\033[" + str(line) + ";0H\033[2K" + 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: ") + decode(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.client.statusDownloads() #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 == 12: # 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(formatSize(speed)+ "/s") + _(" Size: ") + green( download.format_size) + _(" Finished in: ") + green(download.format_eta) + _( " ID: ") + green(download.fid)) line += 1 if download.status == 5: self.println(line, cyan(download.name)) line += 1 self.println(line, _("waiting: ") + green(download.format_wait)) line += 1 self.println(line, "") line += 1 status = self.client.statusServer() if status.pause: self.println(line, _("Status: ") + red("paused") + _(" total Speed: ") + red(formatSize(speed)+ "/s") + _( " Files in queue: ") + red(status.queue)) else: self.println(line, _("Status: ") + red("running") + _(" total Speed: ") + red(formatSize(speed) + "/s") + _( " Files in queue: ") + red(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 not self.pos[0]:# main menu self.println(line, "") line += 1 self.println(line, mag("1.") + _(" Add Links")) line += 1 self.println(line, mag("2.") + _(" Manage 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 if not self.pos[1]: self.println(line, "") line += 1 self.println(line, _("Name your package.")) line += 1 self.println(line, "") line += 1 self.println(line, "") 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, "") else: self.println(line, _("Package: %s") % self.new_package['name']) line += 1 self.println(line, _("Parse the links you want to add.")) line += 1 self.println(line, _("Type %s when done.") % mag("END")) line += 1 self.println(line, _("Links added: ") + mag(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 if not self.pos[1]: pack = self.client.getQueue() self.println(line, _( "Type d(number of package) to delete a package, r to restart, or w/o d,r to look into it.")) line += 1 i = 0 for value in islice(pack, self.pos[2], self.pos[2] + 5): try: self.println(line, mag(str(value.pid)) + ": " + value.name) line += 1 i += 1 except Exception, e: pass for x in range(5 - i): self.println(line, "") line += 1 else: try: pack = self.client.getPackageData(self.pos[1]) except PackageDoesNotExists: self.pos[1] = 0 self.print_input() return self.println(line, _("Type d(number) of the link you want to delete or r(number) to restart.")) line += 1 i = 0 for value in islice(pack.links, self.pos[2], self.pos[2] + 5): try: self.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): 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, 0] self.build_menu() return True if not self.pos[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.client.togglePause() elif inp == "4": self.client.kill() self.client.close() sys.exit() elif inp == "5": os.system('clear') sys.exit() elif self.pos[0] == 1: #add links if not self.pos[1]: self.new_package['name'] = inp self.new_package['links'] = [] self.pos[1] = 1 else: if inp == "END": self.client.addPackage(self.new_package['name'], self.new_package['links'], 1) # add package self.pos = [0, 0, 0] self.links_added = 0 else: #TODO validation self.new_package['links'].append(inp) self.links_added += 1 elif self.pos[0] == 2: #remove links if not self.pos[1]: if inp.startswith("d"): if inp.find("-") > -1: self.client.deletePackages(range(*map(int, inp[1:].split("-")))) else: self.client.deletePackages([int(inp[1:])]) if inp.startswith("r"): self.client.restartPackage(int(inp[1:])) elif inp != "p" and inp != "n": self.pos[1] = int(inp) self.pos[2] = 0 elif inp.startswith('r'): if inp.find("-") > -1: map(self.client.restartFile, range(*map(int, inp[1:].split("-")))) else: self.client.restartFile(int(inp[1:])) elif inp.startswith('d') and inp != "p" and inp != "n": if inp.find("-") > -1: self.client.deleteFiles(range(*map(int, inp[1:].split("-")))) else: self.client.deleteFiles([int(inp[1:])]) if inp == "p": self.pos[2] -= 5 elif inp == "n": self.pos[2] += 5 self.build_menu() def processCommand(self): command = self.command[0] args = [] if len(self.command) > 1: args = self.command[1:] if command == "status": files = self.client.statusDownloads() 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) if download.status == 5: print print_status(download) print "\tWaiting: %s" % download.format_wait 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 ...") return self.client.addPackage(args[0], args[1:], 1) 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 == "pause": self.client.pause() elif command == "unpause": self.client.unpause() elif command == "toggle": self.client.togglePause() elif command == "kill": self.client.kill() else: print_commands() class RefreshThread(threading.Thread): def __init__(self, cli): threading.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: self.cli.println(2, red(str(e))) self.cli.pos[1] = 0 self.cli.pos[2] = 0 print_exc() 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" + unicode(string) + "\033[0m" def green(string): return "\033[1;32m" + unicode(string) + "\033[0m" def yellow(string): return "\033[1;33m" + unicode(string) + "\033[0m" def red(string): return "\033[1;31m" + unicode(string) + "\033[0m" def cyan(string): return "\033[1;36m" + unicode(string) + "\033[0m" def mag(string): return "\033[1;35m" + unicode(string) + "\033[0m" def white(string): return "\033[1;37m" + unicode(string) + "\033[0m" def print_help(): print print "pyLoadCli Copyright (c) 2008-2011 the pyLoad Team" print print "Usage: [python] pyLoadCli.py [options] [command]" print print "" print "See pyLoadCli.py -c for a complete listing." print print "" print " -i, --interactive", " Start in interactive mode" print print " -u, --username=", " " * 2, "Specify Username" print " --pw=", " " * 2, "Password" print " -a, --address=", " "*3, "Specify address (default=127.0.0.1)" print " -p, --port", " " * 7, "Specify port (default=7227)" print 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 ...", _("Adds package to queue")), ("del_file ...", _("Delete Files from Queue/Collector")), ("del_package ...", _("Delete Packages from Queue/Collector")), ("pause", _("Pause the server")), ("unpause", _("continue downloads")), ("toggle", _("Toggle pause/unpause")), ("kill", _("kill server")), ] print _("List of commands:") print for c in commands: print "%-30s %s" % c if __name__ == "__main__": config = ConfigParser() translation = gettext.translation("pyLoadCli", join(pypath, "locale"), languages=["en", config['general']['language']]) translation.install(unicode=True) username = "" password = "" addr = "127.0.0.1" port = 7227 interactive = False command = None shortOptions = 'iu:p:a:hc' longOptions = ['interactive', "username=", "pw=", "address=", "port=", "help", "commands"] 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"): addr = params elif option in ("-p", "--port"): port = int(params) elif option in ("-h", "--help"): print_help() 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() exit() if len(extraparams) >= 1: command = extraparams client = False if interactive: try: client = ThriftClient(addr, port, username, password) except WrongLogin: pass except NoSSL: print _("You need py-openssl to connect to this pyLoad Core.") exit() except NoConnection: addr = False port = False if not client: if not addr: addr = raw_input(_("Address: ")) if not port: port = int(raw_input(_("Port: "))) if not username: username = raw_input(_("Username: ")) if not password: from getpass import getpass password = getpass(_("Password: ")) try: client = ThriftClient(addr, port, username, password) except WrongLogin: print _("Login data is wrong.") except NoConnection: print _("Could not establish connection to %(addr)s:%(port)s." % {"addr": addr, "port" : port }) else: try: client = ThriftClient(addr, port, username, password) except WrongLogin: print _("Login data is required.") except NoConnection: print _("Could not establish connection to %(addr)s:%(port)s." % {"addr": addr, "port" : port }) except NoSSL: print _("You need py-openssl to connect to this pyLoad.") if interactive and command: print _("Interactive mode ignored since you passed some commands.") if client: cli = Cli(client, command)