def dolegacy(arg): if arg and True in arg[0]: self.stdscr.clear() if not self.legacy_mode: self.legacy_mode = Legacy(self.stdscr,self.encoding) component.get("ConsoleUI").set_mode(self.legacy_mode) self.legacy_mode.refresh() curses.curs_set(2) else: self.messages.append(("Error","An error occured trying to switch to legacy mode"))
def find_merge_error(self, image, prob, segmentation, label): ''' Find a merge error in a segment. This generates 50 borders within the segment and ranks them using the CNN. Returns The corrected segment. and Rank (1-p) for the found merge error. 1 If confident .. 0 If uncertain or -1 If there was a problem. ''' # create binary mask binary = Util.threshold(segmentation, label) # here potential boundaries are generated and ranked by the CNN results = Legacy.fix_single_merge(self._cnn, image, prob, binary, N=50, erode=True, invert=True, dilate=True, border_seeds=True, oversampling=False) if len(results) > 0: # sort the prediction/border tuples (prediction, border)-tupels sorted_pred = sorted(results, key=lambda x: x[0]) lowest_rank = sorted_pred[0][0] border = sorted_pred[0][1] corrected = Legacy.correct_merge(segmentation, label, border) return corrected, 1. - lowest_rank else: return -1
def __init__(self, **kwargs): super().__init__(**kwargs, orientation='vertical') self.legacy = Legacy() self.actionbar = ActionBar(pos_hint={'top': 1}) self.av = av = ActionView() av.add_widget(ActionPrevious(title='', with_previous=False)) av.add_widget(ActionOverflow()) backbutton = ActionButton(text='Back') av.add_widget(backbutton) backbutton.bind(on_press=(self.back)) self.nextbutton = ActionButton(text='Next') av.add_widget(self.nextbutton) self.nextbutton.bind(on_press=(self.nextbtn)) self.last_widget = self.monitor = Title(self) self.actionbar.add_widget(av) # can't be set in F.ActionView() -- seems like a bug av.use_separator = True self.add_widget(self.actionbar) self.add_widget(self.monitor) self.av = av
class AllTorrents(BaseMode, component.Component): def __init__(self, stdscr, encoding=None): self.formatted_rows = None self.torrent_names = None self.cursel = 1 self.curoff = 1 # TODO: this should really be 0 indexed self.column_string = "" self.popup = None self.messages = deque() self.marked = [] self.last_mark = -1 self._sorted_ids = None self._go_top = False self._curr_filter = None self.entering_search = False self.search_string = None self.cursor = 0 self.coreconfig = component.get("ConsoleUI").coreconfig self.legacy_mode = None self.__status_dict = {} self.__torrent_info_id = None BaseMode.__init__(self, stdscr, encoding) component.Component.__init__(self, "AllTorrents", 1, depend=["SessionProxy"]) curses.curs_set(0) self.stdscr.notimeout(0) self.__split_help() self.update_config() component.start(["AllTorrents"]) self._info_fields = [ ("Name",None,("name",)), ("State", None, ("state",)), ("Down Speed", format_utils.format_speed, ("download_payload_rate",)), ("Up Speed", format_utils.format_speed, ("upload_payload_rate",)), ("Progress", format_utils.format_progress, ("progress",)), ("ETA", deluge.common.ftime, ("eta",)), ("Path", None, ("save_path",)), ("Downloaded",deluge.common.fsize,("all_time_download",)), ("Uploaded", deluge.common.fsize,("total_uploaded",)), ("Share Ratio", format_utils.format_float, ("ratio",)), ("Seeders",format_utils.format_seeds_peers,("num_seeds","total_seeds")), ("Peers",format_utils.format_seeds_peers,("num_peers","total_peers")), ("Active Time",deluge.common.ftime,("active_time",)), ("Seeding Time",deluge.common.ftime,("seeding_time",)), ("Date Added",deluge.common.fdate,("time_added",)), ("Availability", format_utils.format_float, ("distributed_copies",)), ("Pieces", format_utils.format_pieces, ("num_pieces","piece_length")), ] self.__status_keys = ["name","state","download_payload_rate","upload_payload_rate", "progress","eta","all_time_download","total_uploaded", "ratio", "num_seeds","total_seeds","num_peers","total_peers", "active_time", "seeding_time","time_added","distributed_copies", "num_pieces", "piece_length","save_path"] # component start/update def start(self): component.get("SessionProxy").get_torrents_status(self.__status_dict, self.__status_fields).addCallback(self.set_state,False) def update(self): component.get("SessionProxy").get_torrents_status(self.__status_dict, self.__status_fields).addCallback(self.set_state,True) if self.__torrent_info_id: component.get("SessionProxy").get_torrent_status(self.__torrent_info_id, self.__status_keys).addCallback(self._on_torrent_status) def update_config(self): self.config = ConfigManager("console.conf",DEFAULT_PREFS) self.__cols_to_show = [pref for pref in column_pref_names if self.config["show_%s"%pref]] self.__columns = [prefs_to_names[col] for col in self.__cols_to_show] self.__status_fields = column.get_required_fields(self.__columns) for rf in ["state","name","queue"]: # we always need these, even if we're not displaying them if not rf in self.__status_fields: self.__status_fields.append(rf) self.__update_columns() def __split_help(self): self.__help_lines = format_utils.wrap_string(HELP_STR,(self.cols/2)-2) def resume(self): self._go_top = True component.start(["AllTorrents"]) self.refresh() def __update_columns(self): self.column_widths = [self.config["%s_width"%c] for c in self.__cols_to_show] req = sum(filter(lambda x:x >= 0,self.column_widths)) if (req > self.cols): # can't satisfy requests, just spread out evenly cw = int(self.cols/len(self.__columns)) for i in range(0,len(self.column_widths)): self.column_widths[i] = cw else: rem = self.cols - req var_cols = len(filter(lambda x: x < 0,self.column_widths)) if (var_cols > 0): vw = int(rem/var_cols) for i in range(0, len(self.column_widths)): if (self.column_widths[i] < 0): self.column_widths[i] = vw self.column_string = "{!header!}%s"%("".join(["%s%s"%(self.__columns[i]," "*(self.column_widths[i]-len(self.__columns[i]))) for i in range(0,len(self.__columns))])) def set_state(self, state, refresh): self.curstate = state # cache in case we change sort order newnames = [] newrows = [] self._sorted_ids = self._sort_torrents(self.curstate) for torrent_id in self._sorted_ids: ts = self.curstate[torrent_id] newnames.append(ts["name"]) newrows.append((format_utils.format_row([column.get_column_value(name,ts) for name in self.__columns],self.column_widths),ts["state"])) self.numtorrents = len(state) self.formatted_rows = newrows self.torrent_names = newnames if refresh: self.refresh() def get_torrent_name(self, torrent_id): for p,i in enumerate(self._sorted_ids): if torrent_id == i: return self.torrent_names[p] return None def _scroll_up(self, by): prevoff = self.curoff self.cursel = max(self.cursel - by,1) if ((self.cursel - 1) < self.curoff): self.curoff = max(self.cursel - 1,1) return prevoff != self.curoff def _scroll_down(self, by): prevoff = self.curoff self.cursel = min(self.cursel + by,self.numtorrents) if ((self.curoff + self.rows - 5) < self.cursel): self.curoff = self.cursel - self.rows + 5 return prevoff != self.curoff def current_torrent_id(self): if self._sorted_ids: return self._sorted_ids[self.cursel-1] else: return None def _selected_torrent_ids(self): ret = [] for i in self.marked: ret.append(self._sorted_ids[i-1]) return ret def _on_torrent_status(self, state): if (self.popup): self.popup.clear() name = state["name"] off = int((self.cols/4)-(len(name)/2)) self.popup.set_title(name) for i,f in enumerate(self._info_fields): if f[1] != None: args = [] try: for key in f[2]: args.append(state[key]) except: log.debug("Could not get info field: %s",e) continue info = f[1](*args) else: info = state[f[2][0]] nl = len(f[0])+4 if (nl+len(info))>self.popup.width: self.popup.add_line("{!info!}%s: {!input!}%s"%(f[0],info[:(self.popup.width - nl)])) info = info[(self.popup.width - nl):] n = self.popup.width-3 chunks = [info[i:i+n] for i in xrange(0, len(info), n)] for c in chunks: self.popup.add_line(" %s"%c) else: self.popup.add_line("{!info!}%s: {!input!}%s"%(f[0],info)) self.refresh() else: self.__torrent_info_id = None def on_resize(self, *args): BaseMode.on_resize_norefresh(self, *args) self.__update_columns() self.__split_help() if self.popup: self.popup.handle_resize() self.refresh() def _queue_sort(self, v1, v2): if v1 == v2: return 0 if v2 < 0: return -1 if v1 < 0: return 1 if v1 > v2: return 1 if v2 > v1: return -1 def _sort_torrents(self, state): "sorts by queue #" return sorted(state,cmp=self._queue_sort,key=lambda s:state.get(s)["queue"]) def _format_queue(self, qnum): if (qnum >= 0): return "%d"%(qnum+1) else: return "" def show_torrent_details(self,tid): def dodeets(arg): if arg and True in arg[0]: self.stdscr.clear() component.get("ConsoleUI").set_mode(TorrentDetail(self,tid,self.stdscr,self.encoding)) else: self.messages.append(("Error","An error occured trying to display torrent details")) component.stop(["AllTorrents"]).addCallback(dodeets) def show_preferences(self): def _on_get_config(config): client.core.get_listen_port().addCallback(_on_get_listen_port,config) def _on_get_listen_port(port,config): client.core.get_cache_status().addCallback(_on_get_cache_status,port,config) def _on_get_cache_status(status,port,config): def doprefs(arg): if arg and True in arg[0]: self.stdscr.clear() component.get("ConsoleUI").set_mode(Preferences(self,config,self.config,port,status,self.stdscr,self.encoding)) else: self.messages.append(("Error","An error occured trying to display preferences")) component.stop(["AllTorrents"]).addCallback(doprefs) client.core.get_config().addCallback(_on_get_config) def __show_events(self): def doevents(arg): if arg and True in arg[0]: self.stdscr.clear() component.get("ConsoleUI").set_mode(EventView(self,self.stdscr,self.encoding)) else: self.messages.append(("Error","An error occured trying to display events")) component.stop(["AllTorrents"]).addCallback(doevents) def __legacy_mode(self): def dolegacy(arg): if arg and True in arg[0]: self.stdscr.clear() if not self.legacy_mode: self.legacy_mode = Legacy(self.stdscr,self.encoding) component.get("ConsoleUI").set_mode(self.legacy_mode) self.legacy_mode.refresh() curses.curs_set(2) else: self.messages.append(("Error","An error occured trying to switch to legacy mode")) component.stop(["AllTorrents"]).addCallback(dolegacy) def _torrent_filter(self, idx, data): if data==FILTER.ALL: self.__status_dict = {} self._curr_filter = None elif data==FILTER.ACTIVE: self.__status_dict = {"state":"Active"} self._curr_filter = "Active" elif data==FILTER.DOWNLOADING: self.__status_dict = {"state":"Downloading"} self._curr_filter = "Downloading" elif data==FILTER.SEEDING: self.__status_dict = {"state":"Seeding"} self._curr_filter = "Seeding" elif data==FILTER.PAUSED: self.__status_dict = {"state":"Paused"} self._curr_filter = "Paused" elif data==FILTER.CHECKING: self.__status_dict = {"state":"Checking"} self._curr_filter = "Checking" elif data==FILTER.ERROR: self.__status_dict = {"state":"Error"} self._curr_filter = "Error" elif data==FILTER.QUEUED: self.__status_dict = {"state":"Queued"} self._curr_filter = "Queued" self._go_top = True return True def _show_torrent_filter_popup(self): self.popup = SelectablePopup(self,"Filter Torrents",self._torrent_filter) self.popup.add_line("_All",data=FILTER.ALL) self.popup.add_line("Ac_tive",data=FILTER.ACTIVE) self.popup.add_line("_Downloading",data=FILTER.DOWNLOADING,foreground="green") self.popup.add_line("_Seeding",data=FILTER.SEEDING,foreground="cyan") self.popup.add_line("_Paused",data=FILTER.PAUSED) self.popup.add_line("_Error",data=FILTER.ERROR,foreground="red") self.popup.add_line("_Checking",data=FILTER.CHECKING,foreground="blue") self.popup.add_line("Q_ueued",data=FILTER.QUEUED,foreground="yellow") def __report_add_status(self, succ_cnt, fail_cnt, fail_msgs): if fail_cnt == 0: self.report_message("Torrents Added","{!success!}Successfully added %d torrent(s)"%succ_cnt) else: msg = ("{!error!}Failed to add the following %d torrent(s):\n {!error!}"%fail_cnt)+"\n {!error!}".join(fail_msgs) if succ_cnt != 0: msg += "\n \n{!success!}Successfully added %d torrent(s)"%succ_cnt self.report_message("Torrent Add Report",msg) def _do_add(self, result): log.debug("Adding Torrent(s): %s (dl path: %s) (paused: %d)",result["file"],result["path"],result["add_paused"]) ress = {"succ":0, "fail":0, "fmsg":[]} def fail_cb(msg,t_file,ress): log.debug("failed to add torrent: %s: %s"%(t_file,msg)) ress["fail"]+=1 ress["fmsg"].append("%s: %s"%(t_file,msg)) if (ress["succ"]+ress["fail"]) >= ress["total"]: self.__report_add_status(ress["succ"],ress["fail"],ress["fmsg"]) def suc_cb(tid,t_file,ress): if tid: log.debug("added torrent: %s (%s)"%(t_file,tid)) ress["succ"]+=1 if (ress["succ"]+ress["fail"]) >= ress["total"]: self.__report_add_status(ress["succ"],ress["fail"],ress["fmsg"]) else: fail_cb("Already in session (probably)",t_file,ress) add_torrent(result["file"],result,suc_cb,fail_cb,ress) def _show_torrent_add_popup(self): dl = "" ap = 1 try: dl = self.coreconfig["download_location"] except KeyError: pass try: if self.coreconfig["add_paused"]: ap = 0 except KeyError: pass self.popup = InputPopup(self,"Add Torrent (Esc to cancel)",close_cb=self._do_add) self.popup.add_text_input("Enter path to torrent file:","file") self.popup.add_text_input("Enter save path:","path",dl) self.popup.add_select_input("Add Paused:","add_paused",["Yes","No"],[True,False],ap) self.popup.add_spaces(1) self.popup.add_select_input("Path is:","path_type",["Auto","File","URL"],[0,1,2],0) def report_message(self,title,message): self.messages.append((title,message)) def clear_marks(self): self.marked = [] self.last_mark = -1 def set_popup(self,pu): self.popup = pu self.refresh() def refresh(self,lines=None): #log.error("ref") #import traceback #traceback.print_stack() # Something has requested we scroll to the top of the list if self._go_top: self.cursel = 1 self.curoff = 1 self._go_top = False # show a message popup if there's anything queued if self.popup == None and self.messages: title,msg = self.messages.popleft() self.popup = MessagePopup(self,title,msg) if not lines: self.stdscr.clear() # Update the status bars if self._curr_filter == None: self.add_string(0,self.statusbars.topbar) else: self.add_string(0,"%s {!filterstatus!}Current filter: %s"%(self.statusbars.topbar,self._curr_filter)) self.add_string(1,self.column_string) if self.entering_search: self.add_string(self.rows - 1,"{!black,white!}Search torrents: %s"%self.search_string) else: hstr = "%sPress [h] for help"%(" "*(self.cols - len(self.statusbars.bottombar) - 10)) self.add_string(self.rows - 1, "%s%s"%(self.statusbars.bottombar,hstr)) # add all the torrents if self.formatted_rows == []: msg = "No torrents match filter".center(self.cols) self.add_string(3, "{!info!}%s"%msg) elif self.formatted_rows: tidx = self.curoff currow = 2 if lines: todraw = [] for l in lines: todraw.append(self.formatted_rows[l]) lines.reverse() else: todraw = self.formatted_rows[tidx-1:] for row in todraw: # default style fg = "white" bg = "black" attr = None if lines: tidx = lines.pop()+1 currow = tidx-self.curoff+2 if tidx in self.marked: bg = "blue" attr = "bold" if tidx == self.cursel: bg = "white" attr = "bold" if tidx in self.marked: fg = "blue" else: fg = "black" if row[1] == "Downloading": fg = "green" elif row[1] == "Seeding": fg = "cyan" elif row[1] == "Error": fg = "red" elif row[1] == "Queued": fg = "yellow" elif row[1] == "Checking": fg = "blue" if attr: colorstr = "{!%s,%s,%s!}"%(fg,bg,attr) else: colorstr = "{!%s,%s!}"%(fg,bg) self.add_string(currow,"%s%s"%(colorstr,row[0]),trim=False) tidx += 1 currow += 1 if (currow > (self.rows - 2)): break else: self.add_string(1, "Waiting for torrents from core...") #self.stdscr.redrawwin() if self.entering_search: curses.curs_set(2) self.stdscr.move(self.rows-1,self.cursor+17) else: curses.curs_set(0) self.stdscr.noutrefresh() if self.popup: self.popup.refresh() curses.doupdate() def _mark_unmark(self,idx): if idx in self.marked: self.marked.remove(idx) self.last_mark = -1 else: self.marked.append(idx) self.last_mark = idx def __do_search(self): # search forward for the next torrent matching self.search_string for i,n in enumerate(self.torrent_names[self.cursel:]): if n.find(self.search_string) >= 0: self.cursel += (i+1) if ((self.curoff + self.rows - 5) < self.cursel): self.curoff = self.cursel - self.rows + 5 return def __update_search(self, c): if c == curses.KEY_BACKSPACE or c == 127: if self.search_string and self.cursor > 0: self.search_string = self.search_string[:self.cursor - 1] + self.search_string[self.cursor:] self.cursor-=1 elif c == curses.KEY_DC: if self.search_string and self.cursor < len(self.search_string): self.search_string = self.search_string[:self.cursor] + self.search_string[self.cursor+1:] elif c == curses.KEY_LEFT: self.cursor = max(0,self.cursor-1) elif c == curses.KEY_RIGHT: self.cursor = min(len(self.search_string),self.cursor+1) elif c == curses.KEY_HOME: self.cursor = 0 elif c == curses.KEY_END: self.cursor = len(self.search_string) elif c == 27: self.search_string = None self.entering_search = False elif c == 10 or c == curses.KEY_ENTER: self.entering_search = False if self.search_string: self.__do_search() else: self.search_string = None elif c > 31 and c < 256: stroke = chr(c) uchar = "" while not uchar: try: uchar = stroke.decode(self.encoding) except UnicodeDecodeError: c = self.stdscr.getch() stroke += chr(c) if uchar: if self.cursor == len(self.search_string): self.search_string += uchar else: # Insert into string self.search_string = self.search_string[:self.cursor] + uchar + self.search_string[self.cursor:] # Move the cursor forward self.cursor+=1 def _doRead(self): # Read the character effected_lines = None c = self.stdscr.getch() if self.popup: if self.popup.handle_read(c): self.popup = None self.refresh() return if c > 31 and c < 256: if chr(c) == 'Q': from twisted.internet import reactor if client.connected(): def on_disconnect(result): reactor.stop() client.disconnect().addCallback(on_disconnect) else: reactor.stop() return if self.formatted_rows==None or self.popup: return elif self.entering_search: self.__update_search(c) self.refresh([]) return #log.error("pressed key: %d\n",c) #if c == 27: # handle escape # log.error("CANCEL") # Navigate the torrent list if c == curses.KEY_UP: if self.cursel == 1: return if not self._scroll_up(1): effected_lines = [self.cursel-1,self.cursel] elif c == curses.KEY_PPAGE: self._scroll_up(int(self.rows/2)) elif c == curses.KEY_DOWN: if self.cursel >= self.numtorrents: return if not self._scroll_down(1): effected_lines = [self.cursel-2,self.cursel-1] elif c == curses.KEY_NPAGE: self._scroll_down(int(self.rows/2)) elif c == curses.KEY_HOME: self._scroll_up(self.cursel) elif c == curses.KEY_END: self._scroll_down(self.numtorrents-self.cursel) elif c == curses.KEY_RIGHT: # We enter a new mode for the selected torrent here tid = self.current_torrent_id() if tid: self.show_torrent_details(tid) return # Enter Key elif (c == curses.KEY_ENTER or c == 10) and self.numtorrents: if self.cursel not in self.marked: self.marked.append(self.cursel) self.last_mark = self.cursel torrent_actions_popup(self,self._selected_torrent_ids(),details=True) return else: if c > 31 and c < 256: if chr(c) == '/': self.search_string = "" self.cursor = 0 self.entering_search = True elif chr(c) == 'n' and self.search_string: self.__do_search() elif chr(c) == 'j': if not self._scroll_up(1): effected_lines = [self.cursel-1,self.cursel] elif chr(c) == 'k': if not self._scroll_down(1): effected_lines = [self.cursel-2,self.cursel-1] elif chr(c) == 'i': cid = self.current_torrent_id() if cid: def cb(): self.__torrent_info_id = None self.popup = Popup(self,"Info",close_cb=cb) self.popup.add_line("Getting torrent info...") self.__torrent_info_id = cid elif chr(c) == 'm': self._mark_unmark(self.cursel) effected_lines = [self.cursel-1] elif chr(c) == 'M': if self.last_mark >= 0: if (self.cursel+1) > self.last_mark: mrange = range(self.last_mark,self.cursel+1) else: mrange = range(self.cursel-1,self.last_mark) self.marked.extend(mrange[1:]) effected_lines = mrange else: self._mark_unmark(self.cursel) effected_lines = [self.cursel-1] elif chr(c) == 'c': self.marked = [] self.last_mark = -1 elif chr(c) == 'a': self._show_torrent_add_popup() elif chr(c) == 'f': self._show_torrent_filter_popup() elif chr(c) == 'h': self.popup = Popup(self,"Help",init_lines=self.__help_lines) elif chr(c) == 'p': self.show_preferences() return elif chr(c) == 'e': self.__show_events() return elif chr(c) == 'l': self.__legacy_mode() return self.refresh(effected_lines)
from csp import csp import reports as report_util import faq as faq_util from legacy import Legacy from flask import Flask, request, make_response, jsonify, render_template, redirect, abort, url_for as flask_url_for from flaskext.markdown import Markdown from flask_talisman import Talisman app = Flask(__name__) Markdown(app) Talisman(app, content_security_policy=csp, content_security_policy_nonce_in=['script-src']) legacy_util = Legacy(faq_util) # Overwrite the built-in method. def url_for(endpoint, **kwargs): # Persist the lens parameter across navigations. lens = request.args.get('lens') if report_util.is_valid_lens(lens): kwargs['lens'] = lens # Pass through to the built-in method. return flask_url_for(endpoint, **kwargs) app.jinja_env.globals['url_for'] = url_for @app.route('/') def index():
class AllTorrents(BaseMode, component.Component): def __init__(self, stdscr, encoding=None): self.formatted_rows = None self.torrent_names = None self.cursel = 1 self.curoff = 1 # TODO: this should really be 0 indexed self.column_string = "" self.popup = None self.messages = deque() self.marked = [] self.last_mark = -1 self._sorted_ids = None self._go_top = False self._curr_filter = None self.entering_search = False self.search_string = None self.cursor = 0 self.coreconfig = component.get("ConsoleUI").coreconfig self.legacy_mode = None self.__status_dict = {} self.__torrent_info_id = None BaseMode.__init__(self, stdscr, encoding) component.Component.__init__(self, "AllTorrents", 1, depend=["SessionProxy"]) curses.curs_set(0) self.stdscr.notimeout(0) self.__split_help() self.update_config() component.start(["AllTorrents"]) self._info_fields = [ ("Name",None,("name",)), ("State", None, ("state",)), ("Down Speed", format_utils.format_speed, ("download_payload_rate",)), ("Up Speed", format_utils.format_speed, ("upload_payload_rate",)), ("Progress", format_utils.format_progress, ("progress",)), ("ETA", deluge.common.ftime, ("eta",)), ("Path", None, ("save_path",)), ("Downloaded",deluge.common.fsize,("all_time_download",)), ("Uploaded", deluge.common.fsize,("total_uploaded",)), ("Share Ratio", format_utils.format_float, ("ratio",)), ("Seeders",format_utils.format_seeds_peers,("num_seeds","total_seeds")), ("Peers",format_utils.format_seeds_peers,("num_peers","total_peers")), ("Active Time",deluge.common.ftime,("active_time",)), ("Seeding Time",deluge.common.ftime,("seeding_time",)), ("Date Added",deluge.common.fdate,("time_added",)), ("Availability", format_utils.format_float, ("distributed_copies",)), ("Pieces", format_utils.format_pieces, ("num_pieces","piece_length")), ] self.__status_keys = ["name","state","download_payload_rate","upload_payload_rate", "progress","eta","all_time_download","total_uploaded", "ratio", "num_seeds","total_seeds","num_peers","total_peers", "active_time", "seeding_time","time_added","distributed_copies", "num_pieces", "piece_length","save_path"] # component start/update def start(self): component.get("SessionProxy").get_torrents_status(self.__status_dict, self.__status_fields).addCallback(self.set_state,False) def update(self): component.get("SessionProxy").get_torrents_status(self.__status_dict, self.__status_fields).addCallback(self.set_state,True) if self.__torrent_info_id: component.get("SessionProxy").get_torrent_status(self.__torrent_info_id, self.__status_keys).addCallback(self._on_torrent_status) def update_config(self): self.config = ConfigManager("console.conf",DEFAULT_PREFS) self.__cols_to_show = [pref for pref in column_pref_names if self.config["show_%s"%pref]] self.__columns = [prefs_to_names[col] for col in self.__cols_to_show] self.__status_fields = column.get_required_fields(self.__columns) for rf in ["state","name","queue"]: # we always need these, even if we're not displaying them if not rf in self.__status_fields: self.__status_fields.append(rf) self.__update_columns() def __split_help(self): self.__help_lines = format_utils.wrap_string(HELP_STR,(self.cols/2)-2) def resume(self): self._go_top = True component.start(["AllTorrents"]) self.refresh() def __update_columns(self): self.column_widths = [self.config["%s_width"%c] for c in self.__cols_to_show] req = sum(filter(lambda x:x >= 0,self.column_widths)) if (req > self.cols): # can't satisfy requests, just spread out evenly cw = int(self.cols/len(self.__columns)) for i in range(0,len(self.column_widths)): self.column_widths[i] = cw else: rem = self.cols - req var_cols = len(filter(lambda x: x < 0,self.column_widths)) if (var_cols > 0): vw = int(rem/var_cols) for i in range(0, len(self.column_widths)): if (self.column_widths[i] < 0): self.column_widths[i] = vw self.column_string = "{!header!}%s"%("".join(["%s%s"%(self.__columns[i]," "*(self.column_widths[i]-len(self.__columns[i]))) for i in range(0,len(self.__columns))])) def set_state(self, state, refresh): self.curstate = state # cache in case we change sort order newnames = [] newrows = [] self._sorted_ids = self._sort_torrents(self.curstate) for torrent_id in self._sorted_ids: ts = self.curstate[torrent_id] newnames.append(ts["name"]) newrows.append((format_utils.format_row([column.get_column_value(name,ts) for name in self.__columns],self.column_widths),ts["state"])) self.numtorrents = len(state) self.formatted_rows = newrows self.torrent_names = newnames if refresh: self.refresh() def get_torrent_name(self, torrent_id): for p,i in enumerate(self._sorted_ids): if torrent_id == i: return self.torrent_names[p] return None def _scroll_up(self, by): prevoff = self.curoff self.cursel = max(self.cursel - by,1) if ((self.cursel - 1) < self.curoff): self.curoff = max(self.cursel - 1,1) return prevoff != self.curoff def _scroll_down(self, by): prevoff = self.curoff self.cursel = min(self.cursel + by,self.numtorrents) if ((self.curoff + self.rows - 5) < self.cursel): self.curoff = self.cursel - self.rows + 5 return prevoff != self.curoff def current_torrent_id(self): if self._sorted_ids: return self._sorted_ids[self.cursel-1] else: return None def _selected_torrent_ids(self): ret = [] for i in self.marked: ret.append(self._sorted_ids[i-1]) return ret def _on_torrent_status(self, state): if (self.popup): self.popup.clear() name = state["name"] off = int((self.cols/4)-(len(name)/2)) self.popup.set_title(name) for i,f in enumerate(self._info_fields): if f[1] != None: args = [] try: for key in f[2]: args.append(state[key]) except: log.debug("Could not get info field: %s",e) continue info = f[1](*args) else: info = state[f[2][0]] nl = len(f[0])+4 if (nl+len(info))>self.popup.width: self.popup.add_line("{!info!}%s: {!input!}%s"%(f[0],info[:(self.popup.width - nl)])) info = info[(self.popup.width - nl):] n = self.popup.width-3 chunks = [info[i:i+n] for i in xrange(0, len(info), n)] for c in chunks: self.popup.add_line(" %s"%c) else: self.popup.add_line("{!info!}%s: {!input!}%s"%(f[0],info)) self.refresh() else: self.__torrent_info_id = None def on_resize(self, *args): BaseMode.on_resize_norefresh(self, *args) self.__update_columns() self.__split_help() if self.popup: self.popup.handle_resize() self.refresh() def _queue_sort(self, v1, v2): if v1 == v2: return 0 if v2 < 0: return -1 if v1 < 0: return 1 if v1 > v2: return 1 if v2 > v1: return -1 def _sort_torrents(self, state): "sorts by queue #" return sorted(state,cmp=self._queue_sort,key=lambda s:state.get(s)["queue"]) def _format_queue(self, qnum): if (qnum >= 0): return "%d"%(qnum+1) else: return "" def show_torrent_details(self,tid): def dodeets(arg): if arg and True in arg[0]: self.stdscr.clear() component.get("ConsoleUI").set_mode(TorrentDetail(self,tid,self.stdscr,self.encoding)) else: self.messages.append(("Error","An error occured trying to display torrent details")) component.stop(["AllTorrents"]).addCallback(dodeets) def show_preferences(self): def _on_get_config(config): client.core.get_listen_port().addCallback(_on_get_listen_port,config) def _on_get_listen_port(port,config): client.core.get_cache_status().addCallback(_on_get_cache_status,port,config) def _on_get_cache_status(status,port,config): def doprefs(arg): if arg and True in arg[0]: self.stdscr.clear() component.get("ConsoleUI").set_mode(Preferences(self,config,self.config,port,status,self.stdscr,self.encoding)) else: self.messages.append(("Error","An error occured trying to display preferences")) component.stop(["AllTorrents"]).addCallback(doprefs) client.core.get_config().addCallback(_on_get_config) def __show_events(self): def doevents(arg): if arg and True in arg[0]: self.stdscr.clear() component.get("ConsoleUI").set_mode(EventView(self,self.stdscr,self.encoding)) else: self.messages.append(("Error","An error occured trying to display events")) component.stop(["AllTorrents"]).addCallback(doevents) def __legacy_mode(self): def dolegacy(arg): if arg and True in arg[0]: self.stdscr.clear() if not self.legacy_mode: self.legacy_mode = Legacy(self.stdscr,self.encoding) component.get("ConsoleUI").set_mode(self.legacy_mode) self.legacy_mode.refresh() curses.curs_set(2) else: self.messages.append(("Error","An error occured trying to switch to legacy mode")) component.stop(["AllTorrents"]).addCallback(dolegacy) def _torrent_filter(self, idx, data): if data==FILTER.ALL: self.__status_dict = {} self._curr_filter = None elif data==FILTER.ACTIVE: self.__status_dict = {"state":"Active"} self._curr_filter = "Active" elif data==FILTER.DOWNLOADING: self.__status_dict = {"state":"Downloading"} self._curr_filter = "Downloading" elif data==FILTER.SEEDING: self.__status_dict = {"state":"Seeding"} self._curr_filter = "Seeding" elif data==FILTER.PAUSED: self.__status_dict = {"state":"Paused"} self._curr_filter = "Paused" elif data==FILTER.CHECKING: self.__status_dict = {"state":"Checking"} self._curr_filter = "Checking" elif data==FILTER.ERROR: self.__status_dict = {"state":"Error"} self._curr_filter = "Error" elif data==FILTER.QUEUED: self.__status_dict = {"state":"Queued"} self._curr_filter = "Queued" self._go_top = True return True def _show_torrent_filter_popup(self): self.popup = SelectablePopup(self,"Filter Torrents",self._torrent_filter) self.popup.add_line("_All",data=FILTER.ALL) self.popup.add_line("Ac_tive",data=FILTER.ACTIVE) self.popup.add_line("_Downloading",data=FILTER.DOWNLOADING,foreground="green") self.popup.add_line("_Seeding",data=FILTER.SEEDING,foreground="cyan") self.popup.add_line("_Paused",data=FILTER.PAUSED) self.popup.add_line("_Error",data=FILTER.ERROR,foreground="red") self.popup.add_line("_Checking",data=FILTER.CHECKING,foreground="blue") self.popup.add_line("Q_ueued",data=FILTER.QUEUED,foreground="yellow") def __report_add_status(self, succ_cnt, fail_cnt, fail_msgs): if fail_cnt == 0: self.report_message("Torrents Added","{!success!}Sucessfully added %d torrent(s)"%succ_cnt) else: msg = ("{!error!}Failed to add the following %d torrent(s):\n {!error!}"%fail_cnt)+"\n {!error!}".join(fail_msgs) if succ_cnt != 0: msg += "\n \n{!success!}Sucessfully added %d torrent(s)"%succ_cnt self.report_message("Torrent Add Report",msg) def _do_add(self, result): log.debug("Adding Torrent(s): %s (dl path: %s) (paused: %d)",result["file"],result["path"],result["add_paused"]) ress = {"succ":0, "fail":0, "fmsg":[]} def fail_cb(msg,t_file,ress): log.debug("failed to add torrent: %s: %s"%(t_file,msg)) ress["fail"]+=1 ress["fmsg"].append("%s: %s"%(t_file,msg)) if (ress["succ"]+ress["fail"]) >= ress["total"]: self.__report_add_status(ress["succ"],ress["fail"],ress["fmsg"]) def suc_cb(tid,t_file,ress): if tid: log.debug("added torrent: %s (%s)"%(t_file,tid)) ress["succ"]+=1 if (ress["succ"]+ress["fail"]) >= ress["total"]: self.__report_add_status(ress["succ"],ress["fail"],ress["fmsg"]) else: fail_cb("Already in session (probably)",t_file,ress) add_torrent(result["file"],result,suc_cb,fail_cb,ress) def _show_torrent_add_popup(self): dl = "" ap = 1 try: dl = self.coreconfig["download_location"] except KeyError: pass try: if self.coreconfig["add_paused"]: ap = 0 except KeyError: pass self.popup = InputPopup(self,"Add Torrent (Esc to cancel)",close_cb=self._do_add) self.popup.add_text_input("Enter path to torrent file:","file") self.popup.add_text_input("Enter save path:","path",dl) self.popup.add_select_input("Add Paused:","add_paused",["Yes","No"],[True,False],ap) self.popup.add_spaces(1) self.popup.add_select_input("Path is:","path_type",["Auto","File","URL"],[0,1,2],0) def report_message(self,title,message): self.messages.append((title,message)) def clear_marks(self): self.marked = [] self.last_mark = -1 def set_popup(self,pu): self.popup = pu self.refresh() def refresh(self,lines=None): #log.error("ref") #import traceback #traceback.print_stack() # Something has requested we scroll to the top of the list if self._go_top: self.cursel = 1 self.curoff = 1 self._go_top = False # show a message popup if there's anything queued if self.popup == None and self.messages: title,msg = self.messages.popleft() self.popup = MessagePopup(self,title,msg) if not lines: self.stdscr.clear() # Update the status bars if self._curr_filter == None: self.add_string(0,self.statusbars.topbar) else: self.add_string(0,"%s {!filterstatus!}Current filter: %s"%(self.statusbars.topbar,self._curr_filter)) self.add_string(1,self.column_string) if self.entering_search: self.add_string(self.rows - 1,"{!black,white!}Search torrents: %s"%self.search_string) else: hstr = "%sPress [h] for help"%(" "*(self.cols - len(self.statusbars.bottombar) - 10)) self.add_string(self.rows - 1, "%s%s"%(self.statusbars.bottombar,hstr)) # add all the torrents if self.formatted_rows == []: msg = "No torrents match filter".center(self.cols) self.add_string(3, "{!info!}%s"%msg) elif self.formatted_rows: tidx = self.curoff currow = 2 if lines: todraw = [] for l in lines: todraw.append(self.formatted_rows[l]) lines.reverse() else: todraw = self.formatted_rows[tidx-1:] for row in todraw: # default style fg = "white" bg = "black" attr = None if lines: tidx = lines.pop()+1 currow = tidx-self.curoff+2 if tidx in self.marked: bg = "blue" attr = "bold" if tidx == self.cursel: bg = "white" attr = "bold" if tidx in self.marked: fg = "blue" else: fg = "black" if row[1] == "Downloading": fg = "green" elif row[1] == "Seeding": fg = "cyan" elif row[1] == "Error": fg = "red" elif row[1] == "Queued": fg = "yellow" elif row[1] == "Checking": fg = "blue" if attr: colorstr = "{!%s,%s,%s!}"%(fg,bg,attr) else: colorstr = "{!%s,%s!}"%(fg,bg) self.add_string(currow,"%s%s"%(colorstr,row[0]),trim=False) tidx += 1 currow += 1 if (currow > (self.rows - 2)): break else: self.add_string(1, "Waiting for torrents from core...") #self.stdscr.redrawwin() if self.entering_search: curses.curs_set(2) self.stdscr.move(self.rows-1,self.cursor+17) else: curses.curs_set(0) self.stdscr.noutrefresh() if self.popup: self.popup.refresh() curses.doupdate() def _mark_unmark(self,idx): if idx in self.marked: self.marked.remove(idx) self.last_mark = -1 else: self.marked.append(idx) self.last_mark = idx def __do_search(self): # search forward for the next torrent matching self.search_string for i,n in enumerate(self.torrent_names[self.cursel:]): if n.find(self.search_string) >= 0: self.cursel += (i+1) if ((self.curoff + self.rows - 5) < self.cursel): self.curoff = self.cursel - self.rows + 5 return def __update_search(self, c): if c == curses.KEY_BACKSPACE or c == 127: if self.search_string and self.cursor > 0: self.search_string = self.search_string[:self.cursor - 1] + self.search_string[self.cursor:] self.cursor-=1 elif c == curses.KEY_DC: if self.search_string and self.cursor < len(self.search_string): self.search_string = self.search_string[:self.cursor] + self.search_string[self.cursor+1:] elif c == curses.KEY_LEFT: self.cursor = max(0,self.cursor-1) elif c == curses.KEY_RIGHT: self.cursor = min(len(self.search_string),self.cursor+1) elif c == curses.KEY_HOME: self.cursor = 0 elif c == curses.KEY_END: self.cursor = len(self.search_string) elif c == 27: self.search_string = None self.entering_search = False elif c == 10 or c == curses.KEY_ENTER: self.entering_search = False if self.search_string: self.__do_search() else: self.search_string = None elif c > 31 and c < 256: stroke = chr(c) uchar = "" while not uchar: try: uchar = stroke.decode(self.encoding) except UnicodeDecodeError: c = self.stdscr.getch() stroke += chr(c) if uchar: if self.cursor == len(self.search_string): self.search_string += uchar else: # Insert into string self.search_string = self.search_string[:self.cursor] + uchar + self.search_string[self.cursor:] # Move the cursor forward self.cursor+=1 def _doRead(self): # Read the character effected_lines = None c = self.stdscr.getch() if self.popup: if self.popup.handle_read(c): self.popup = None self.refresh() return if c > 31 and c < 256: if chr(c) == 'Q': from twisted.internet import reactor if client.connected(): def on_disconnect(result): reactor.stop() client.disconnect().addCallback(on_disconnect) else: reactor.stop() return if self.formatted_rows==None or self.popup: return elif self.entering_search: self.__update_search(c) self.refresh([]) return #log.error("pressed key: %d\n",c) #if c == 27: # handle escape # log.error("CANCEL") # Navigate the torrent list if c == curses.KEY_UP: if self.cursel == 1: return if not self._scroll_up(1): effected_lines = [self.cursel-1,self.cursel] elif c == curses.KEY_PPAGE: self._scroll_up(int(self.rows/2)) elif c == curses.KEY_DOWN: if self.cursel >= self.numtorrents: return if not self._scroll_down(1): effected_lines = [self.cursel-2,self.cursel-1] elif c == curses.KEY_NPAGE: self._scroll_down(int(self.rows/2)) elif c == curses.KEY_HOME: self._scroll_up(self.cursel) elif c == curses.KEY_END: self._scroll_down(self.numtorrents-self.cursel) elif c == curses.KEY_RIGHT: # We enter a new mode for the selected torrent here tid = self.current_torrent_id() if tid: self.show_torrent_details(tid) return # Enter Key elif (c == curses.KEY_ENTER or c == 10) and self.numtorrents: if self.cursel not in self.marked: self.marked.append(self.cursel) self.last_mark = self.cursel torrent_actions_popup(self,self._selected_torrent_ids(),details=True) return else: if c > 31 and c < 256: if chr(c) == '/': self.search_string = "" self.cursor = 0 self.entering_search = True elif chr(c) == 'n' and self.search_string: self.__do_search() elif chr(c) == 'j': if not self._scroll_up(1): effected_lines = [self.cursel-1,self.cursel] elif chr(c) == 'k': if not self._scroll_down(1): effected_lines = [self.cursel-2,self.cursel-1] elif chr(c) == 'i': cid = self.current_torrent_id() if cid: def cb(): self.__torrent_info_id = None self.popup = Popup(self,"Info",close_cb=cb) self.popup.add_line("Getting torrent info...") self.__torrent_info_id = cid elif chr(c) == 'm': self._mark_unmark(self.cursel) effected_lines = [self.cursel-1] elif chr(c) == 'M': if self.last_mark >= 0: if (self.cursel+1) > self.last_mark: mrange = range(self.last_mark,self.cursel+1) else: mrange = range(self.cursel-1,self.last_mark) self.marked.extend(mrange[1:]) effected_lines = mrange else: self._mark_unmark(self.cursel) effected_lines = [self.cursel-1] elif chr(c) == 'c': self.marked = [] self.last_mark = -1 elif chr(c) == 'a': self._show_torrent_add_popup() elif chr(c) == 'f': self._show_torrent_filter_popup() elif chr(c) == 'h': self.popup = Popup(self,"Help",init_lines=self.__help_lines) elif chr(c) == 'p': self.show_preferences() return elif chr(c) == 'e': self.__show_events() return elif chr(c) == 'l': self.__legacy_mode() return self.refresh(effected_lines)
def __init__(self, stdscr, encoding=None): self.torrent_names = None self.numtorrents = -1 self._cached_rows = {} self.cursel = 1 self.curoff = 1 # TODO: this should really be 0 indexed self.column_string = "" self.popup = None self.messages = deque() self.marked = [] self.last_mark = -1 self._sorted_ids = None self._go_top = False self._curr_filter = None self.entering_search = False self.search_string = None self.search_state = SEARCH_EMPTY self.coreconfig = component.get("ConsoleUI").coreconfig self.legacy_mode = None self.__status_dict = {} self.__torrent_info_id = None BaseMode.__init__(self, stdscr, encoding) component.Component.__init__(self, "AllTorrents", 1, depend=["SessionProxy"]) curses.curs_set(0) self.stdscr.notimeout(0) self.update_config() component.start(["AllTorrents"]) self._info_fields = [ ("Name",None,("name",)), ("State", None, ("state",)), ("Down Speed", format_utils.format_speed, ("download_payload_rate",)), ("Up Speed", format_utils.format_speed, ("upload_payload_rate",)), ("Progress", format_utils.format_progress, ("progress",)), ("ETA", deluge.common.ftime, ("eta",)), ("Path", None, ("save_path",)), ("Downloaded",deluge.common.fsize,("all_time_download",)), ("Uploaded", deluge.common.fsize,("total_uploaded",)), ("Share Ratio", format_utils.format_float, ("ratio",)), ("Seeders",format_utils.format_seeds_peers,("num_seeds","total_seeds")), ("Peers",format_utils.format_seeds_peers,("num_peers","total_peers")), ("Active Time",deluge.common.ftime,("active_time",)), ("Seeding Time",deluge.common.ftime,("seeding_time",)), ("Date Added",deluge.common.fdate,("time_added",)), ("Availability", format_utils.format_float, ("distributed_copies",)), ("Pieces", format_utils.format_pieces, ("num_pieces","piece_length")), ] self.__status_keys = ["name","state","download_payload_rate","upload_payload_rate", "progress","eta","all_time_download","total_uploaded", "ratio", "num_seeds","total_seeds","num_peers","total_peers", "active_time", "seeding_time","time_added","distributed_copies", "num_pieces", "piece_length","save_path"] self.legacy_mode = Legacy(self.stdscr, self.encoding) if self.config["first_run"]: self.popup = Popup(self,"Welcome to Deluge" ,init_lines=self.__help_lines, height_req=0.75, width_req=65) self.config["first_run"] = False self.config.save()
class AllTorrents(BaseMode, component.Component): def __init__(self, stdscr, encoding=None): self.torrent_names = None self.numtorrents = -1 self._cached_rows = {} self.cursel = 1 self.curoff = 1 # TODO: this should really be 0 indexed self.column_string = "" self.popup = None self.messages = deque() self.marked = [] self.last_mark = -1 self._sorted_ids = None self._go_top = False self._curr_filter = None self.entering_search = False self.search_string = None self.search_state = SEARCH_EMPTY self.coreconfig = component.get("ConsoleUI").coreconfig self.legacy_mode = None self.__status_dict = {} self.__torrent_info_id = None BaseMode.__init__(self, stdscr, encoding) component.Component.__init__(self, "AllTorrents", 1, depend=["SessionProxy"]) curses.curs_set(0) self.stdscr.notimeout(0) self.update_config() component.start(["AllTorrents"]) self._info_fields = [ ("Name",None,("name",)), ("State", None, ("state",)), ("Down Speed", format_utils.format_speed, ("download_payload_rate",)), ("Up Speed", format_utils.format_speed, ("upload_payload_rate",)), ("Progress", format_utils.format_progress, ("progress",)), ("ETA", deluge.common.ftime, ("eta",)), ("Path", None, ("save_path",)), ("Downloaded",deluge.common.fsize,("all_time_download",)), ("Uploaded", deluge.common.fsize,("total_uploaded",)), ("Share Ratio", format_utils.format_float, ("ratio",)), ("Seeders",format_utils.format_seeds_peers,("num_seeds","total_seeds")), ("Peers",format_utils.format_seeds_peers,("num_peers","total_peers")), ("Active Time",deluge.common.ftime,("active_time",)), ("Seeding Time",deluge.common.ftime,("seeding_time",)), ("Date Added",deluge.common.fdate,("time_added",)), ("Availability", format_utils.format_float, ("distributed_copies",)), ("Pieces", format_utils.format_pieces, ("num_pieces","piece_length")), ] self.__status_keys = ["name","state","download_payload_rate","upload_payload_rate", "progress","eta","all_time_download","total_uploaded", "ratio", "num_seeds","total_seeds","num_peers","total_peers", "active_time", "seeding_time","time_added","distributed_copies", "num_pieces", "piece_length","save_path"] self.legacy_mode = Legacy(self.stdscr, self.encoding) if self.config["first_run"]: self.popup = Popup(self,"Welcome to Deluge" ,init_lines=self.__help_lines, height_req=0.75, width_req=65) self.config["first_run"] = False self.config.save() # component start/update def start(self): component.get("SessionProxy").get_torrents_status(self.__status_dict, self.__status_fields).addCallback(self.set_state,False) def update(self): component.get("SessionProxy").get_torrents_status(self.__status_dict, self.__status_fields).addCallback(self.set_state,True) if self.__torrent_info_id: component.get("SessionProxy").get_torrent_status(self.__torrent_info_id, self.__status_keys).addCallback(self._on_torrent_status) def update_config(self): self.config = ConfigManager("console.conf",DEFAULT_PREFS) s_primary = self.config["sort_primary"] s_secondary = self.config["sort_secondary"] self.__cols_to_show = [ pref for pref in column_pref_names if ("show_%s" % pref) not in self.config or self.config["show_%s"%pref] ] self.__columns = [prefs_to_names[col] for col in self.__cols_to_show] self.__status_fields = column.get_required_fields(self.__columns) # we always need these, even if we're not displaying them for rf in ["state", "name", "queue", "progress"]: if rf not in self.__status_fields: self.__status_fields.append(rf) # same with sort keys if s_primary and (s_primary not in self.__status_fields): self.__status_fields.append(s_primary) if s_secondary and (s_secondary not in self.__status_fields): self.__status_fields.append(s_secondary) self.__update_columns() def resume(self): component.start(["AllTorrents"]) self.refresh() def __update_columns(self): self.column_widths = [self.config["%s_width"%c] for c in self.__cols_to_show] req = sum(filter(lambda x:x >= 0,self.column_widths)) if (req > self.cols): # can't satisfy requests, just spread out evenly cw = int(self.cols/len(self.__columns)) for i in range(0,len(self.column_widths)): self.column_widths[i] = cw else: rem = self.cols - req var_cols = len(filter(lambda x: x < 0,self.column_widths)) if (var_cols > 0): vw = int(rem/var_cols) for i in range(0, len(self.column_widths)): if (self.column_widths[i] < 0): self.column_widths[i] = vw self.column_string = "{!header!}" try: primary_sort_col_name = prefs_to_names[self.config["sort_primary"]] except: primary_sort_col_name = "" for i, column in enumerate(self.__columns): ccol = column width = self.column_widths[i] #Trim the column if it's too long to fit if len(ccol) > width: ccol = ccol[:width - 1] # Padding ccol += " " * (width - len(ccol)) # Highlight the primary sort column if column == primary_sort_col_name: if i != len(self.__columns) - 1: ccol = "{!black,green,bold!}%s{!header!}" % ccol else: ccol = ("{!black,green,bold!}%s" % ccol)[:-1] self.column_string += ccol def set_state(self, state, refresh): self.curstate = state # cache in case we change sort order newnames = [] self._cached_rows = {} self._sorted_ids = self._sort_torrents(self.curstate) for torrent_id in self._sorted_ids: ts = self.curstate[torrent_id] newnames.append(ts["name"]) self.numtorrents = len(state) self.torrent_names = newnames if refresh: self.refresh() def get_torrent_name(self, torrent_id): for p,i in enumerate(self._sorted_ids): if torrent_id == i: return self.torrent_names[p] return None def _scroll_up(self, by): prevoff = self.curoff self.cursel = max(self.cursel - by,1) if ((self.cursel - 1) < self.curoff): self.curoff = max(self.cursel - 1,1) return prevoff != self.curoff def _scroll_down(self, by): prevoff = self.curoff self.cursel = min(self.cursel + by,self.numtorrents) if ((self.curoff + self.rows - 5) < self.cursel): self.curoff = self.cursel - self.rows + 5 return prevoff != self.curoff def current_torrent_id(self): if self._sorted_ids: return self._sorted_ids[self.cursel-1] else: return None def _selected_torrent_ids(self): ret = [] for i in self.marked: ret.append(self._sorted_ids[i-1]) return ret def _on_torrent_status(self, state): if (self.popup): self.popup.clear() name = state["name"] off = int((self.cols/4)-(len(name)/2)) self.popup.set_title(name) for i,f in enumerate(self._info_fields): if f[1] != None: args = [] try: for key in f[2]: args.append(state[key]) except: log.debug("Could not get info field: %s",e) continue info = f[1](*args) else: info = state[f[2][0]] nl = len(f[0])+4 if (nl+len(info))>self.popup.width: self.popup.add_line("{!info!}%s: {!input!}%s"%(f[0],info[:(self.popup.width - nl)])) info = info[(self.popup.width - nl):] n = self.popup.width-3 chunks = [info[i:i+n] for i in xrange(0, len(info), n)] for c in chunks: self.popup.add_line(" %s"%c) else: self.popup.add_line("{!info!}%s: {!input!}%s"%(f[0],info)) self.refresh() else: self.__torrent_info_id = None def on_resize(self, *args): BaseMode.on_resize_norefresh(self, *args) if self.popup: self.popup.handle_resize() self.update() self.__update_columns() self.refresh([]) def _queue_sort(self, v1, v2): if v1 == v2: return 0 if v2 < 0: return -1 if v1 < 0: return 1 if v1 > v2: return 1 if v2 > v1: return -1 def _sort_torrents(self, state): "sorts by primary and secondary sort fields" if not state: return {} s_primary = self.config["sort_primary"] s_secondary = self.config["sort_secondary"] result = state #Sort first by secondary sort field and then primary sort field # so it all works out cmp_func = self._queue_sort sg = state.get def sort_by_field(state, result, field): if field in column_names_to_state_keys: field = column_names_to_state_keys[field] reverse = field in reverse_sort_fields #Get first element so we can check if it has given field # and if it's a string first_element = state[state.keys()[0]] if field in first_element: is_string = isinstance( first_element[field], basestring) sort_key = lambda s:sg(s)[field] sort_key2 = lambda s:sg(s)[field].lower() #If it's a string, sort case-insensitively but preserve A>a order if is_string: result = sorted(result, cmp_func, sort_key, reverse) result = sorted(result, cmp_func, sort_key2, reverse) else: result = sorted(result, cmp_func, sort_key, reverse) if field == "eta": result = sorted(result, key=lambda s: state.get(s)["eta"] == 0) return result #Just in case primary and secondary fields are empty and/or # both are too ambiguous, also sort by queue position first if "queue" not in [s_secondary, s_primary]: result = sort_by_field(state, result, "queue") if s_secondary != s_primary: result = sort_by_field(state, result, s_secondary) result = sort_by_field(state, result, s_primary) if self.config["separate_complete"]: result = sorted(result, cmp_func, lambda s: state.get(s)["progress"] == 100.0) return result def _format_queue(self, qnum): if (qnum >= 0): return "%d"%(qnum+1) else: return "" def show_addtorrents_screen(self): def dodeets(arg): if arg and True in arg[0]: self.stdscr.erase() component.get("ConsoleUI").set_mode(AddTorrents(self,self.stdscr, self.config, self.encoding)) else: self.messages.append(("Error","An error occured trying to display add torrents screen")) component.stop(["AllTorrents"]).addCallback(dodeets) def show_torrent_details(self,tid): def dodeets(arg): if arg and True in arg[0]: self.stdscr.erase() component.get("ConsoleUI").set_mode(TorrentDetail(self,tid,self.stdscr, self.config, self.encoding)) else: self.messages.append(("Error","An error occured trying to display torrent details")) component.stop(["AllTorrents"]).addCallback(dodeets) def show_preferences(self): def _on_get_config(config): client.core.get_listen_port().addCallback(_on_get_listen_port,config) def _on_get_listen_port(port,config): client.core.get_cache_status().addCallback(_on_get_cache_status,port,config) def _on_get_cache_status(status,port,config): def doprefs(arg): if arg and True in arg[0]: self.stdscr.erase() component.get("ConsoleUI").set_mode(Preferences(self,config,self.config,port,status,self.stdscr,self.encoding)) else: self.messages.append(("Error","An error occured trying to display preferences")) component.stop(["AllTorrents"]).addCallback(doprefs) client.core.get_config().addCallback(_on_get_config) def __show_events(self): def doevents(arg): if arg and True in arg[0]: self.stdscr.erase() component.get("ConsoleUI").set_mode(EventView(self,self.stdscr,self.encoding)) else: self.messages.append(("Error","An error occured trying to display events")) component.stop(["AllTorrents"]).addCallback(doevents) def __legacy_mode(self): def dolegacy(arg): if arg and True in arg[0]: self.stdscr.erase() component.get("ConsoleUI").set_mode(self.legacy_mode) self.legacy_mode.refresh() curses.curs_set(2) else: self.messages.append(("Error","An error occured trying to switch to legacy mode")) component.stop(["AllTorrents"]).addCallback(dolegacy) def _torrent_filter(self, idx, data): if data==FILTER.ALL: self.__status_dict = {} self._curr_filter = None elif data==FILTER.ACTIVE: self.__status_dict = {"state":"Active"} self._curr_filter = "Active" elif data==FILTER.DOWNLOADING: self.__status_dict = {"state":"Downloading"} self._curr_filter = "Downloading" elif data==FILTER.SEEDING: self.__status_dict = {"state":"Seeding"} self._curr_filter = "Seeding" elif data==FILTER.PAUSED: self.__status_dict = {"state":"Paused"} self._curr_filter = "Paused" elif data==FILTER.CHECKING: self.__status_dict = {"state":"Checking"} self._curr_filter = "Checking" elif data==FILTER.ERROR: self.__status_dict = {"state":"Error"} self._curr_filter = "Error" elif data==FILTER.QUEUED: self.__status_dict = {"state":"Queued"} self._curr_filter = "Queued" self._go_top = True return True def _show_torrent_filter_popup(self): self.popup = SelectablePopup(self,"Filter Torrents", self._torrent_filter) self.popup.add_line("_All",data=FILTER.ALL) self.popup.add_line("Ac_tive",data=FILTER.ACTIVE) self.popup.add_line("_Downloading",data=FILTER.DOWNLOADING,foreground="green") self.popup.add_line("_Seeding",data=FILTER.SEEDING,foreground="cyan") self.popup.add_line("_Paused",data=FILTER.PAUSED) self.popup.add_line("_Error",data=FILTER.ERROR,foreground="red") self.popup.add_line("_Checking",data=FILTER.CHECKING,foreground="blue") self.popup.add_line("Q_ueued",data=FILTER.QUEUED,foreground="yellow") def _report_add_status(self, succ_cnt, fail_cnt, fail_msgs): if fail_cnt == 0: self.report_message("Torrents Added","{!success!}Successfully added %d torrent(s)"%succ_cnt) else: msg = ("{!error!}Failed to add the following %d torrent(s):\n {!input!}"%fail_cnt)+"\n ".join(fail_msgs) if succ_cnt != 0: msg += "\n \n{!success!}Successfully added %d torrent(s)"%succ_cnt self.report_message("Torrent Add Report",msg) def _show_torrent_add_popup(self): def do_add_from_url(result): def fail_cb(msg, url): log.debug("failed to add torrent: %s: %s" % (url, msg)) error_msg = "{!input!} * %s: {!error!}%s" % (url, msg) self._report_add_status(0, 1, [error_msg] ) def success_cb(tid, url): if tid: log.debug("added torrent: %s (%s)"%(url, tid)) self._report_add_status(1, 0, []) else: fail_cb("Already in session (probably)", url) url = result["url"] if not url: return t_options = { "download_location": result["path"], "add_paused": result["add_paused"] } if deluge.common.is_magnet(url): client.core.add_torrent_magnet(url, t_options).addCallback(success_cb, url).addErrback(fail_cb, url) elif deluge.common.is_url(url): client.core.add_torrent_url(url, t_options).addCallback(success_cb, url).addErrback(fail_cb, url) else: self.messages.append(("Error","{!error!}Invalid URL or magnet link: %s" % url)) return log.debug("Adding Torrent(s): %s (dl path: %s) (paused: %d)", url, result["path"], result["add_paused"]) def show_add_url_popup(): try: dl = self.coreconfig["download_location"] except KeyError: dl = "" ap = 1 try: if self.coreconfig["add_paused"]: ap = 0 except KeyError: pass self.popup = InputPopup(self,"Add Torrent (Esc to cancel)", close_cb=do_add_from_url) self.popup.add_text_input("Enter torrent URL or Magnet link:", "url") self.popup.add_text_input("Enter save path:", "path", dl) self.popup.add_select_input("Add Paused:", "add_paused", ["Yes", "No"], [True, False], ap) def option_chosen(index, data): self.popup = None if not data: return if data == 1: self.show_addtorrents_screen() elif data == 2: show_add_url_popup() self.popup = SelectablePopup(self,"Add torrent", option_chosen) self.popup.add_line("From _File(s)", data=1) self.popup.add_line("From _URL or Magnet", data=2) self.popup.add_line("_Cancel", data=0) def _do_set_column_visibility(self, data): for key, value in data.items(): self.config[key] = value self.config.save() self.update_config() self.__update_columns() self.refresh([]) def _show_visible_columns_popup(self): title = "Visible columns (Enter to exit)" self.popup = InputPopup(self, title, close_cb=self._do_set_column_visibility, immediate_action=True, height_req= len(column_pref_names) + 1, width_req= max([len(col) for col in column_pref_names + [title]]) + 8 ) for col in column_pref_names: name = prefs_to_names[col] prop = "show_%s" % col if prop not in self.config: continue state = self.config[prop] self.popup.add_checked_input(name, prop, state) def report_message(self,title,message): self.messages.append((title,message)) def clear_marks(self): self.marked = [] self.last_mark = -1 def set_popup(self,pu): self.popup = pu self.refresh() def refresh(self,lines=None): #log.error("ref") #import traceback #traceback.print_stack() # Something has requested we scroll to the top of the list if self._go_top: self.cursel = 1 self.curoff = 1 self._go_top = False # show a message popup if there's anything queued if self.popup == None and self.messages: title,msg = self.messages.popleft() self.popup = MessagePopup(self,title,msg, width_req=1.0) if not lines: if component.get("ConsoleUI").screen != self: return self.stdscr.erase() # Update the status bars if self._curr_filter == None: self.add_string(0,self.statusbars.topbar) else: self.add_string(0,"%s {!filterstatus!}Current filter: %s"%(self.statusbars.topbar,self._curr_filter)) self.add_string(1,self.column_string) if self.entering_search: string = { SEARCH_EMPTY: "{!black,white!}Search torrents: %s{!black,white!}", SEARCH_SUCCESS: "{!black,white!}Search torrents: {!black,green!}%s{!black,white!}", SEARCH_FAILING: "{!black,white!}Search torrents: {!black,red!}%s{!black,white!}", SEARCH_START_REACHED: "{!black,white!}Search torrents: {!black,yellow!}%s{!black,white!} (start reached)", SEARCH_END_REACHED: "{!black,white!}Search torrents: {!black,yellow!}%s{!black,white!} (end reached)" }[self.search_state] % self.search_string self.add_string(self.rows - 1, string) else: #This will quite likely fail when switching modes try: rf = format_utils.remove_formatting string = self.statusbars.bottombar hstr = "Press {!magenta,blue,bold!}[h]{!status!} for help" string += " " * ( self.cols - len(rf(string)) - len(rf(hstr))) + hstr self.add_string(self.rows - 1, string) except: pass # add all the torrents if self.numtorrents == 0: msg = "No torrents match filter".center(self.cols) self.add_string(3, "{!info!}%s"%msg) elif self.numtorrents > 0: tidx = self.curoff currow = 2 #Because dots are slow sorted_ids = self._sorted_ids curstate = self.curstate gcv = column.get_column_value fr = format_utils.format_row cols = self.__columns colw = self.column_widths cr = self._cached_rows def draw_row(index): if index not in cr: ts = curstate[sorted_ids[index]] cr[index] = (fr([gcv(name,ts) for name in cols],colw),ts["state"]) return cr[index] if lines: todraw = [] for l in lines: if l < tidx - 1: continue if l >= tidx - 1 + self.rows - 3: break if l >= self.numtorrents: break todraw.append(draw_row(l)) lines.reverse() else: todraw = [] for i in range(tidx-1, tidx-1 + self.rows - 3): if i >= self.numtorrents: break todraw += [draw_row(i)] for row in todraw: # default style fg = "white" bg = "black" attr = None if lines: tidx = lines.pop()+1 currow = tidx-self.curoff+2 if tidx in self.marked: bg = "blue" attr = "bold" if tidx == self.cursel: bg = "white" attr = "bold" if tidx in self.marked: fg = "blue" else: fg = "black" if row[1] == "Downloading": fg = "green" elif row[1] == "Seeding": fg = "cyan" elif row[1] == "Error": fg = "red" elif row[1] == "Queued": fg = "yellow" elif row[1] == "Checking": fg = "blue" if self.entering_search and len(self.search_string) > 1: lcase_name = self.torrent_names[tidx-1].lower() sstring_lower = self.search_string.lower() if lcase_name.find(sstring_lower) != -1: if tidx == self.cursel: pass elif tidx in self.marked: bg = "magenta" else: bg = "green" if fg == "green": fg = "black" attr = "bold" if attr: colorstr = "{!%s,%s,%s!}"%(fg,bg,attr) else: colorstr = "{!%s,%s!}"%(fg,bg) try: self.add_string(currow,"%s%s"%(colorstr,row[0]),trim=False) except: #Yeah, this should be fixed in some better way pass tidx += 1 currow += 1 if (currow > (self.rows - 2)): break else: self.add_string(1, "Waiting for torrents from core...") #self.stdscr.redrawwin() if self.entering_search: curses.curs_set(2) self.stdscr.move(self.rows-1, len(self.search_string)+17) else: curses.curs_set(0) if component.get("ConsoleUI").screen != self: return self.stdscr.noutrefresh() if self.popup: self.popup.refresh() curses.doupdate() def _mark_unmark(self,idx): if idx in self.marked: self.marked.remove(idx) self.last_mark = -1 else: self.marked.append(idx) self.last_mark = idx def __search_match_count(self): match_count = 0 search_string = self.search_string.lower() for n in self.torrent_names: n = n.lower() if n.find(search_string) != -1: match_count += 1 return match_count def __do_search(self, direction="first", skip=0): """ Performs a search on visible torrent and sets cursor to the match :param string: direction, the direction of search, can be first, last, next or previous :returns: Nothing """ if direction == "first": search_space = enumerate(self.torrent_names) elif direction == "last": search_space = enumerate(self.torrent_names) search_space = list(search_space) search_space = reversed(search_space) elif direction == "next": search_space = enumerate(self.torrent_names) search_space = list(search_space) search_space = search_space[self.cursel:] elif direction == "previous": search_space = enumerate(self.torrent_names) search_space = list(search_space)[:self.cursel-1] search_space = reversed(search_space) search_string = self.search_string.lower() for i,n in search_space: n = n.lower() if n.find(search_string) != -1: if skip > 0: skip -= 1 continue self.cursel = (i+1) if ((self.curoff + self.rows - 5) < self.cursel): self.curoff = self.cursel - self.rows + 5 elif ((self.curoff +1) > self.cursel): self.curoff = max(1, self.cursel - 1) self.search_state = SEARCH_SUCCESS return if direction in ["first", "last"]: self.search_state = SEARCH_FAILING elif direction == "next": self.search_state = SEARCH_END_REACHED elif direction == "previous": self.search_state = SEARCH_START_REACHED def __update_search(self, c): cname = self.torrent_names[self.cursel-1] if c == curses.KEY_BACKSPACE or c == 127: if self.search_string: self.search_string = self.search_string[:-1] if cname.lower().find(self.search_string.lower()) != -1: self.search_state = SEARCH_SUCCESS else: self.entering_search = False self.search_state = SEARCH_EMPTY self.refresh([]) elif c == curses.KEY_DC: self.search_string = "" self.search_state = SEARCH_SUCCESS self.refresh([]) elif c == curses.KEY_UP: self.__do_search("previous") self.refresh([]) elif c == curses.KEY_DOWN: self.__do_search("next") self.refresh([]) elif c == curses.KEY_LEFT: self.entering_search = False self.search_state = SEARCH_EMPTY self.refresh([]) elif c == ord('/'): self.entering_search = False self.search_state = SEARCH_EMPTY self.refresh([]) elif c == curses.KEY_RIGHT: tid = self.current_torrent_id() self.show_torrent_details(tid) elif c == curses.KEY_HOME: self.__do_search("first") self.refresh([]) elif c == curses.KEY_END: self.__do_search("last") self.refresh([]) elif c in [10, curses.KEY_ENTER]: self.last_mark = -1 tid = self.current_torrent_id() torrent_actions_popup(self, [tid] ,details=True) elif c == 27: self.search_string = "" self.search_state = SEARCH_EMPTY self.refresh([]) elif c > 31 and c < 256: old_search_string = self.search_string stroke = chr(c) uchar = "" while not uchar: try: uchar = stroke.decode(self.encoding) except UnicodeDecodeError: c = self.stdscr.getch() stroke += chr(c) if uchar: self.search_string += uchar still_matching = ( cname.lower().find(self.search_string.lower()) == cname.lower().find(old_search_string.lower()) and cname.lower().find(self.search_string.lower()) != -1 ) if self.search_string and not still_matching: self.__do_search() elif self.search_string: self.search_state = SEARCH_SUCCESS self.refresh([]) if not self.search_string: self.search_state = SEARCH_EMPTY self.refresh([]) def _doRead(self): # Read the character effected_lines = None c = self.stdscr.getch() if self.popup: if self.popup.handle_read(c): self.popup = None self.refresh() return if c > 31 and c < 256: if chr(c) == 'Q': from twisted.internet import reactor if client.connected(): def on_disconnect(result): reactor.stop() client.disconnect().addCallback(on_disconnect) else: reactor.stop() return if self.numtorrents < 0: return elif self.entering_search: self.__update_search(c) return if c == curses.KEY_UP: if self.cursel == 1: return if not self._scroll_up(1): effected_lines = [self.cursel-1,self.cursel] elif c == curses.KEY_PPAGE: self._scroll_up(int(self.rows/2)) elif c == curses.KEY_DOWN: if self.cursel >= self.numtorrents: return if not self._scroll_down(1): effected_lines = [self.cursel-2,self.cursel-1] elif c == curses.KEY_NPAGE: self._scroll_down(int(self.rows/2)) elif c == curses.KEY_HOME: self._scroll_up(self.cursel) elif c == curses.KEY_END: self._scroll_down(self.numtorrents-self.cursel) elif c == curses.KEY_DC: if self.cursel not in self.marked: self.marked.append(self.cursel) self.last_mark = self.cursel torrent_actions_popup(self,self._selected_torrent_ids(), action=ACTION.REMOVE) elif c == curses.KEY_RIGHT: # We enter a new mode for the selected torrent here tid = self.current_torrent_id() if tid: self.show_torrent_details(tid) return # Enter Key elif (c == curses.KEY_ENTER or c == 10) and self.numtorrents: if self.cursel not in self.marked: self.marked.append(self.cursel) self.last_mark = self.cursel torrent_actions_popup(self,self._selected_torrent_ids(),details=True) return else: if c > 31 and c < 256: if chr(c) == '/': self.search_string = "" self.entering_search = True elif chr(c) == 'n' and self.search_string: self.__do_search("next") elif chr(c) == 'j': if not self._scroll_up(1): effected_lines = [self.cursel-1,self.cursel] elif chr(c) == 'k': if not self._scroll_down(1): effected_lines = [self.cursel-2,self.cursel-1] elif chr(c) == 'i': cid = self.current_torrent_id() if cid: def cb(): self.__torrent_info_id = None self.popup = Popup(self,"Info",close_cb=cb, height_req=20) self.popup.add_line("Getting torrent info...") self.__torrent_info_id = cid elif chr(c) == 'm': self._mark_unmark(self.cursel) effected_lines = [self.cursel-1] elif chr(c) == 'M': if self.last_mark >= 0: if (self.cursel+1) > self.last_mark: mrange = range(self.last_mark,self.cursel+1) else: mrange = range(self.cursel-1,self.last_mark) self.marked.extend(mrange[1:]) effected_lines = mrange else: self._mark_unmark(self.cursel) effected_lines = [self.cursel-1] elif chr(c) == 'c': self.marked = [] self.last_mark = -1 elif chr(c) == 'a': self._show_torrent_add_popup() elif chr(c) == 'v': self._show_visible_columns_popup() elif chr(c) == 'o': if not self.marked: self.marked = [self.cursel] self.last_mark = self.cursel else: self.last_mark = -1 torrent_actions_popup(self, self._selected_torrent_ids(), action=ACTION.TORRENT_OPTIONS) elif chr(c) == '<': i = len(self.__cols_to_show) try: i = self.__cols_to_show.index(self.config["sort_primary"]) - 1 except: pass i = max(0, i) i = min(len(self.__cols_to_show) - 1, i) self.config["sort_primary"] = self.__cols_to_show[i] self.config.save() self.update_config() self.__update_columns() self.refresh([]) elif chr(c) == '>': i = 0 try: i = self.__cols_to_show.index(self.config["sort_primary"]) + 1 except: pass i = min(len(self.__cols_to_show) - 1, i) i = max(0, i) self.config["sort_primary"] = self.__cols_to_show[i] self.config.save() self.update_config() self.__update_columns() self.refresh([]) elif chr(c) == 'f': self._show_torrent_filter_popup() elif chr(c) == 'h': self.popup = MessagePopup(self, "Help", HELP_STR, width_req=0.75) elif chr(c) == 'p': self.show_preferences() return elif chr(c) == 'e': self.__show_events() return elif chr(c) == 'l': self.__legacy_mode() return self.refresh(effected_lines)