#!/usr/bin/env python from btqueue import * from btqueueget import do_download import os, sys, select, time, signal children = {} childdata = {} states = {} settings = {} killed = {} def read_queue(): fs = os.listdir(queue_dir) ms = {} for f in fs: ms[f] = os.stat(queue_dir + "/" + f).st_mtime def compare(a, b): i = cmp(ms[a], ms[b]) if i != 0: return i return cmp(a, b) fs.sort(compare) return fs def start_child(file): print "starting", file (r, w) = os.pipe() sys.stdout.flush() sys.stderr.flush() pid = os.fork() if pid == 0: for fd in children.keys(): os.close(fd) os.close(r) do_download(queue_dir + "/" + file, w, settings) os.close(w) children[r] = (file, pid) states[r] = {} childdata[r] = "" def trimfile(name, width): name = name.replace(".torrent", "") name = name.replace(".rar", "") name = name.replace(".avi", "") name = name.replace(".ogm", "") if len(name) < width: return name else: return ".." + name[-(width - 2):] def run(): last_display = 0 while 1: global settings settings = read_settings() queue = read_queue() max_downloads = int(settings["max-downloads"]) should_run = queue[:max_downloads] running = {} for fd in children.keys(): (file, pid) = children[fd] if file in should_run: running[file] = 1 else: os.kill(pid, signal.SIGINT) killed[pid] = 1 for file in should_run: if not running.has_key(file): start_child(file) now = time.time() display_period = int(settings["display-period"]) if display_period != 0 and (now - last_display) >= display_period: last_display = now print "\n\n" print "currently downloading:" print "%6s %-10s %6s %5s %10s %6s %6s %s" % ("pid", "state", "pct", "size", "eta", "up", "down", "file") for fd in children: (file, pid) = children[fd] (state, pct, size, eta, up, down) = ("-", 0, "-", "-", 0, 0) if states.has_key(fd): s = states[fd] if s.has_key("state"): state = s["state"] if s.has_key("pct"): pct = float(s["pct"]) if s.has_key("size"): size = str(long(s["size"]) / (1L << 20)) if s.has_key("eta"): eta = s["eta"] if s.has_key("up"): up = float(s["up"]) if s.has_key("down"): down = float(s["down"]) file = trimfile(file, 40) print "%6d %-10s %5.1f%% %5s %10s %6.1f %6.1f %s" % (pid, state, pct, size, eta, up, down, file) print len(queue), "in queue" sys.stdout.flush() write_state_to = settings["write-state-to"] if write_state_to != "": f = open(write_state_to + ".new", "w") for fd in children: (file, pid) = children[fd] state = states.get(fd, {}) s = " ".join(map(lambda p: p[0] + "=" + p[1], state.items())) print >>f, "running %d %s %s" % (pid, file, s) f.close() os.rename(write_state_to + ".new", write_state_to) (rfds, wfds, efds) = select.select(children.keys(), [], [], 2.0) for fd in rfds: (file, pid) = children[fd] data = os.read(fd, 65536) if data == "": os.waitpid(pid, 0) os.close(fd) od = failed_dir if states[fd].has_key("state"): state = states[fd]["state"] else: state = "unknown" if state == "seed": od = complete_dir print "seed", file elif state == "interrupt": print "interrupted", file else: print "failed", file del children[fd] del states[fd] del childdata[fd] if state != "seed" and killed.has_key(pid): del killed[pid] state = "interrupt" if state != "interrupt": os.rename(queue_dir + "/" + file, od + "/" + file) else: childdata[fd] += data while 1: pos = childdata[fd].find("\n") if pos == -1: break states[fd] = {} for i in childdata[fd][:pos].split(" "): (k, v) = i.split("=") states[fd][k] = v childdata[fd] = childdata[fd][pos + 1:] if __name__ == "__main__": try: run() except KeyboardInterrupt: for fd in children: (file, pid) = children[fd] try: os.kill(pid, signal.SIGINT) except: pass