#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#Copyright (C) 2010 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
from getopt import getopt
import gettext
import os
import os.path
from os.path import abspath
from os.path import dirname
from os.path import join
from os.path import exists
from os.path import expanduser
from os import name as platform
import sys
from sys import exit
import threading
import time
from time import sleep
import xmlrpclib
from module.XMLConfigParser import XMLConfigParser
if sys.stdout.encoding.lower().startswith("utf"):
conv = unicode
else:
conv = str
class pyLoadCli:
def __init__(self, server_url, add=False):
self.core = xmlrpclib.ServerProxy(server_url, allow_none=True)
self.getch = _Getch()
self.input = ""
self.pos = [0, 0, 0]
self.inputline = 0
self.menuline = 0
self.new_package = {}
try:
self.core.get_server_version()
except:
print _("pyLoadCore not running")
exit()
if add:
self.core.add_package(add, [add])
print _("Linklist added")
exit()
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()
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(conv(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 format_size(self, size):
return conv(size / 1024) + " MiB"
def println(self, line, content):
print "\033[" + conv(line) + ";0H\033[2K" + conv(content) + "\033[" + conv((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(conv(percent) + "%") + _(" Speed: ") + green(conv(int(download['speed'])) + " kb/s") + _(" Size: ") + green(self.format_size(download['size'])) + _(" Finished in: ") + green(self.format_time(download['eta'])) + _(" ID: ") + green(conv(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(conv(int(speed)) + " kb/s") + _(" Files in queue: ") + red(conv(status["queue"])))
else:
self.println(line, _("Status: ") + red("running") + _(" total Speed: ") + red(conv(int(speed)) + " kb/s") + _(" Files in queue: ") + red(conv(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.") + _(" 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 self.pos[1] == 0:
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(conv(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 self.pos[1] == 0:
pack = self.core.get_queue()
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 id in range(self.pos[2], self.pos[2] + 5):
try:
self.println(line, mag(conv(pack[id]['id'])) + ": " + pack[id]['package_name'])
line += 1
i += 1
except Exception, e:
pass
for x in range(5-i):
self.println(line, "")
line += 1
else:
links = self.core.get_package_files(self.pos[1])
self.println(line, _("Type d(number) of the link you want to delete or r(number) to restart."))
line += 1
i = 0
for id in range(self.pos[2], self.pos[2] + 5):
try:
link = self.core.get_file_info(links[id])
if not link['status_filename']:
self.println(line, mag(conv(link['id'])) + ": " + link['url'])
else:
self.println(line, mag(conv(link['id'])) + ": %s | %s | %s" % (link['filename'], link['status_type'], link['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 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 self.pos[1] == 0:
self.new_package['name'] = inp
self.new_package['links'] = []
self.pos[1] = 1
else:
if inp == "END":
self.core.add_package(self.new_package['name'], self.new_package['links']) # 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 self.pos[1] == 0:
if inp.startswith("d"):
if inp.find("-") > -1:
self.core.del_packages(range(*map(int, inp[1:].split("-"))))
else:
self.core.del_packages([int(inp[1:])])
if inp.startswith("r"):
self.core.restart_package(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.core.restart_file, range(*map(int, inp[1:].split("-"))))
else:
self.core.restart_file(int(inp[1:]))
elif inp.startswith('d') and inp != "p" and inp != "n":
if inp.find("-") > -1:
self.core.del_links(range(*map(int, inp[1:].split("-"))))
else:
self.core.del_links([int(inp[1:])])
if inp == "p":
self.pos[2] -= 5
elif inp == "n":
self.pos[2] += 5
self.build_menu()
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 Exception, e:
self.cli.println(2, red(conv(e)))
self.cli.pos[1] = 0
self.cli.pos[2] = 0
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"
def print_help():
print ""
print "pyLoadCli Copyright (c) 2008-2010 the pyLoad Team"
print ""
print "Usage: [python] pyLoadCli.py [options] [server url]"
print ""
print ""
print "The server url has this format: http://username:passwort@address:port"
print ""
print ""
print " -l, --local", " " * 6, "Use the local settings in config file"
print " -u, --username="
print " " * 20, "Specify username"
print ""
print " -a, --address="
print " " * 20, "Specify address (default=127.0.0.1)"
print ""
print " --linklist="
print " " * 20, "Add container to pyLoad"
print ""
print " -p, --port", " " * 7, "Specify port (default=7272)"
print " -s, --ssl", " " * 8, "Enable ssl (default=off)"
print " -h, --help", " " * 7, "Display this help screen"
print ""
def get_config_path():
try:
from win32com.shell import shellcon, shell
homedir = shell.SHGetFolderPath(0, shellcon.CSIDL_APPDATA, 0, 0)
except ImportError: # quick semi-nasty fallback for non-windows/win32com case
if platform == 'nt':
import ctypes
from ctypes import wintypes, windll
CSIDL_APPDATA = 26
_SHGetFolderPath = ctypes.windll.shell32.SHGetFolderPathW
_SHGetFolderPath.argtypes = [ctypes.wintypes.HWND,
ctypes.c_int,
ctypes.wintypes.HANDLE,
ctypes.wintypes.DWORD, ctypes.wintypes.LPCWSTR]
path_buf = ctypes.wintypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH)
result = _SHGetFolderPath(0, CSIDL_APPDATA, 0, 0, path_buf)
homedir = path_buf.value
else:
homedir = expanduser("~")
if platform == "posix":
configdir = join(homedir, ".config", "pyload")
else:
configdir = join(homedir, "pyLoad")
return join(configdir, "core.xml")
if __name__ == "__main__":
xmlconfig = XMLConfigParser(get_config_path())
config = xmlconfig.getConfig()
translation = gettext.translation("pyLoadCli", join(abspath(dirname(__file__)), "locale"), languages=[config['general']['language']])
translation.install(unicode=(True if sys.stdout.encoding.lower().startswith("utf") else False))
server_url = ""
username = ""
password = ""
addr = ""
port = ""
ssl = None
add = ""
if len(sys.argv) > 1:
addr = "127.0.0.1"
port = "7272"
ssl = ""
shortOptions = 'lu:a:p:s:h'
longOptions = ['local', "username=", "address=", "port=", "ssl=", "help", "linklist=", "pw="]
try:
opts, extraparams = getopt(sys.argv[1:], shortOptions, longOptions)
for option, params in opts:
if option in ("-l", "--local"):
if config['ssl']['activated']:
ssl = "s"
username = config['remote']['username']
password = config['remote']['password']
addr = config['remote']['listenaddr']
port = config['remote']['port']
elif option in ("-u", "--username"):
username = params
elif option in ("-a", "--address"):
addr = params
elif option in ("-p", "--port"):
port = params
elif option in ("-s", "--ssl"):
if params.lower() == "true":
ssl = "s"
elif option in ("-h", "--help"):
print_help()
exit()
elif option in ("--linklist"):
add = params
elif option in ("--pw"):
password = params
except GetoptError:
print 'Unknown Argument(s) "%s"' % " ".join(sys.argv[1:])
print_help()
exit()
if len(extraparams) == 1:
server_url = sys.argv[1]
if not server_url:
if not username: username = raw_input(_("Username: "))
if not addr: addr = raw_input(_("address: "))
if ssl == None:
ssl = raw_input(_("Use SSL? ([y]/n): "))
if ssl == "y" or ssl == "":
ssl = "s"
else:
ssl = ""
if not port: port = raw_input(_("Port: "))
if not password:
from getpass import getpass
password = getpass(_("Password: "))
server_url = "http%s://%s:%s@%s:%s/" % (ssl, username, password, addr, port)
#print server_url
if add:
cli = pyLoadCli(server_url, add)
else:
cli = pyLoadCli(server_url)