summaryrefslogtreecommitdiffstats
path: root/pyLoadCli.py
diff options
context:
space:
mode:
authorGravatar mkaay <mkaay@mkaay.de> 2009-11-25 18:55:38 +0100
committerGravatar mkaay <mkaay@mkaay.de> 2009-11-25 18:55:38 +0100
commit1d3b74dc93d403783924c654cc22f3304a1d7f0a (patch)
tree5e5ef2fed10c48dedf1445b70170a86cbf324e12 /pyLoadCli.py
parentWebinterface with XMLRPC (diff)
downloadpyload-1d3b74dc93d403783924c654cc22f3304a1d7f0a.tar.xz
basic curses cli
Diffstat (limited to 'pyLoadCli.py')
-rwxr-xr-xpyLoadCli.py527
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()