def save_torrent_options(self, row=None): # Keeps the torrent options dictionary up-to-date with what the user has # selected. if row is None: if self.previous_selected_torrent and self.torrent_liststore.iter_is_valid( self.previous_selected_torrent): row = self.previous_selected_torrent else: return torrent_id = self.torrent_liststore.get_value(row, 0) if torrent_id in self.options: options = self.options[torrent_id] else: options = {} options['download_location'] = decode_bytes( self.download_location_path_chooser.get_text()) options['move_completed_path'] = decode_bytes( self.move_completed_path_chooser.get_text()) options['pre_allocate_storage'] = self.builder.get_object( 'chk_pre_alloc').get_active() options['move_completed'] = self.builder.get_object( 'chk_move_completed').get_active() options['max_download_speed'] = self.builder.get_object( 'spin_maxdown').get_value() options['max_upload_speed'] = self.builder.get_object( 'spin_maxup').get_value() options['max_connections'] = self.builder.get_object( 'spin_maxconnections').get_value_as_int() options['max_upload_slots'] = self.builder.get_object( 'spin_maxupslots').get_value_as_int() options['add_paused'] = self.builder.get_object( 'chk_paused').get_active() options['prioritize_first_last_pieces'] = self.builder.get_object( 'chk_prioritize').get_active() options['sequential_download'] = ( self.builder.get_object('chk_sequential_download').get_active() or False) options['move_completed'] = self.builder.get_object( 'chk_move_completed').get_active() options['seed_mode'] = self.builder.get_object( 'chk_seed_mode').get_active() options['super_seeding'] = self.builder.get_object( 'chk_super_seeding').get_active() self.options[torrent_id] = options # Save the file priorities files_priorities = self.build_priorities( self.files_treestore.get_iter_first(), {}) if len(files_priorities) > 0: for i, file_dict in enumerate(self.files[torrent_id]): file_dict['download'] = files_priorities[i]
def on_button_file_clicked(self, widget): log.debug('on_button_file_clicked') # Setup the filechooserdialog chooser = Gtk.FileChooserDialog( _('Choose a .torrent file'), None, Gtk.FileChooserAction.OPEN, buttons=( _('_Cancel'), Gtk.ResponseType.CANCEL, _('_Open'), Gtk.ResponseType.OK, ), ) chooser.set_transient_for(self.dialog) chooser.set_select_multiple(True) chooser.set_property('skip-taskbar-hint', True) chooser.set_local_only(False) # Add .torrent and * file filters file_filter = Gtk.FileFilter() file_filter.set_name(_('Torrent files')) file_filter.add_pattern('*.' + 'torrent') chooser.add_filter(file_filter) file_filter = Gtk.FileFilter() file_filter.set_name(_('All files')) file_filter.add_pattern('*') chooser.add_filter(file_filter) # Load the 'default_load_path' from the config self.config = ConfigManager('gtk3ui.conf') if ('default_load_path' in self.config and self.config['default_load_path'] is not None): chooser.set_current_folder(self.config['default_load_path']) # Run the dialog response = chooser.run() if response == Gtk.ResponseType.OK: result = [decode_bytes(f) for f in chooser.get_filenames()] self.config['default_load_path'] = decode_bytes( chooser.get_current_folder()) else: chooser.destroy() return chooser.destroy() self.add_from_files(result)
def _on_get_torrent_status(self, status): # Check to see if we got valid data from the core if not status: return # Update all the label widgets for widget in self.tab_widgets.values(): txt = self.widget_status_as_fstr(widget, status) if decode_bytes(widget[0].get_text()) != txt: widget[0].set_text(txt) # Update progress bar separately as it's a special case (not a label). fraction = status['progress'] / 100 if self.config['show_piecesbar']: if self.piecesbar.get_fraction() != fraction: self.piecesbar.set_fraction(fraction) if ( status['state'] != 'Checking' and self.piecesbar.get_pieces() != status['pieces'] ): # Skip pieces assignment if checking torrent. self.piecesbar.set_pieces(status['pieces'], status['num_pieces']) self.piecesbar.update() else: if self.progressbar.get_fraction() != fraction: self.progressbar.set_fraction(fraction)
def render_cell_data(self, column, cell, model, row, data): cat = model.get_value(row, 0) label = decode_bytes(model.get_value(row, 2)) count = model.get_value(row, 3) # Suppress Warning: g_object_set_qdata: assertion `G_IS_OBJECT (object)' failed original_filters = warnings.filters[:] warnings.simplefilter('ignore') try: pix = model.get_value(row, 4) finally: warnings.filters = original_filters self.cell_pix.set_property('visible', True if pix else False) if cat == 'cat': self.cell_count.set_property('visible', False) cell.set_padding(10, 2) label = '<b>%s</b>' % label else: count_txt = '<small>%s</small>' % count self.cell_count.set_property('markup', count_txt) self.cell_count.set_property('visible', True) cell.set_padding(2, 1) cell.set_property('markup', label)
def handle_alerts(self): """ Pops all libtorrent alerts in the session queue and handles them appropriately. """ alerts = self.session.pop_alerts() if not alerts: return num_alerts = len(alerts) if log.isEnabledFor(logging.DEBUG): log.debug('Alerts queued: %s', num_alerts) if num_alerts > 0.9 * self.alert_queue_size: log.warning('Warning total alerts queued, %s, passes 90%% of queue size.', num_alerts) # Loop through all alerts in the queue for alert in alerts: alert_type = type(alert).__name__ # Display the alert message if log.isEnabledFor(logging.DEBUG): log.debug('%s: %s', alert_type, decode_bytes(alert.message())) # Call any handlers for this alert type if alert_type in self.handlers: for handler in self.handlers[alert_type]: if log.isEnabledFor(logging.DEBUG): log.debug('Handling alert: %s', alert_type) self.delayed_calls.append(reactor.callLater(0, handler, alert))
def on_button_folder_clicked(self, widget): log.debug('on_button_folder_clicked') # Setup the filechooserdialog chooser = Gtk.FileChooserDialog( _('Choose a folder'), self.dialog, Gtk.FileChooserAction.SELECT_FOLDER, buttons=( _('_Cancel'), Gtk.ResponseType.CANCEL, _('_Open'), Gtk.ResponseType.OK, ), ) chooser.set_transient_for(self.dialog) chooser.set_select_multiple(False) chooser.set_property('skip-taskbar-hint', True) # Run the dialog response = chooser.run() if response == Gtk.ResponseType.OK: result = chooser.get_filename() else: chooser.destroy() return path = decode_bytes(result) self.files_treestore.clear() self.files_treestore.append( None, [result, 'document-open-symbolic', get_path_size(path)]) self.adjust_piece_size() chooser.destroy()
def handle_alerts(self): """ Pops all libtorrent alerts in the session queue and handles them appropriately. """ alerts = self.session.pop_alerts() if not alerts: return num_alerts = len(alerts) if log.isEnabledFor(logging.DEBUG): log.debug('Alerts queued: %s', num_alerts) if num_alerts > 0.9 * self.alert_queue_size: log.warning( 'Warning total alerts queued, %s, passes 90%% of queue size.', num_alerts) # Loop through all alerts in the queue for alert in alerts: alert_type = type(alert).__name__ # Display the alert message if log.isEnabledFor(logging.DEBUG): log.debug('%s: %s', alert_type, decode_bytes(alert.message())) # Call any handlers for this alert type if alert_type in self.handlers: for handler in self.handlers[alert_type]: if log.isEnabledFor(logging.DEBUG): log.debug('Handling alert: %s', alert_type) self.delayed_calls.append( reactor.callLater(0, handler, alert))
def on_dialog_response(response_id): if response_id == Gtk.ResponseType.OK: if ( self.config['tray_password'] == sha(decode_bytes(dialog.get_password()).encode()).hexdigest() ): quit_gtkui()
def readranges(self): """Yields each ip range from the file""" blocklist = self.open() for line in blocklist: line = decode_bytes(line) if not self.is_ignored(line): yield self.parse(line) blocklist.close()
def process_args(args): """Process arguments sent to already running Deluge""" # Make sure args is a list args = list(args) log.debug('Processing args from other process: %s', args) if not client.connected(): # We're not connected so add these to the queue log.debug('Not connected to host.. Adding to queue.') component.get('QueuedTorrents').add_to_queue(args) return config = ConfigManager('gtkui.conf') for arg in args: if not arg.strip(): continue log.debug('arg: %s', arg) if is_url(arg): log.debug('Attempting to add url (%s) from external source...', arg) if config['interactive_add']: component.get('AddTorrentDialog').add_from_url(arg) component.get('AddTorrentDialog').show( config['focus_add_dialog']) else: client.core.add_torrent_url(arg, None) elif is_magnet(arg): log.debug('Attempting to add magnet (%s) from external source...', arg) if config['interactive_add']: component.get('AddTorrentDialog').add_from_magnets([arg]) component.get('AddTorrentDialog').show( config['focus_add_dialog']) else: client.core.add_torrent_magnet(arg, {}) else: log.debug('Attempting to add file (%s) from external source...', arg) if urlparse(arg).scheme == 'file': arg = url2pathname(urlparse(arg).path) path = os.path.abspath(decode_bytes(arg)) if not os.path.exists(path): log.error('No such file: %s', path) continue if config['interactive_add']: component.get('AddTorrentDialog').add_from_files([path]) component.get('AddTorrentDialog').show( config['focus_add_dialog']) else: with open(path, 'rb') as _file: filedump = b64encode(_file.read()) client.core.add_torrent_file( os.path.split(path)[-1], filedump, None)
def update_trackerlist_from_url(self): if self.config["dynamic_trackerlist_url"]: now = datetime.datetime.utcnow() last_update = datetime.datetime.utcfromtimestamp( self.config["last_dynamic_trackers_update"]) if now - last_update > datetime.timedelta( days=self.config["dynamic_trackers_update_interval"]): try: headers = { 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.3', 'Accept-Encoding': 'none', 'Accept-Language': 'en-US,en;q=0.8', } req = urllib.request.Request( self.config["dynamic_trackerlist_url"], headers=headers) try: page = urllib.request.urlopen( req, context=ssl._create_unverified_context()).read() except: # maybe an older Python version without a "context" argument page = urllib.request.urlopen(req).read() new_trackers = [ decode_bytes(url) for url in re.findall(b'\w+://[\w\-.:/]+', page) if is_url(decode_bytes(url)) ] if new_trackers: # replace all existing trackers self.config["trackers"] = [] for new_tracker in new_trackers: self.config["trackers"].append( {"url": new_tracker}) self.config["last_dynamic_trackers_update"] = time.mktime( now.timetuple()) except: traceback.print_exc() return self.config.config
def on_menuitem_toggled(self, widget): """Callback for the generated column menuitems.""" # Get the column name from the widget name = widget.get_child().get_text() # Set the column's visibility based on the widgets active state try: self.columns[name].column.set_visible(widget.get_active()) except KeyError: self.columns[decode_bytes(name)].column.set_visible(widget.get_active()) return
def on_menuitem_toggled(self, widget): """Callback for the generated column menuitems.""" # Get the column name from the widget name = widget.get_child().get_text() # Set the column's visibility based on the widgets active state try: self.columns[name].column.set_visible(widget.get_active()) except KeyError: self.columns[decode_bytes(name)].column.set_visible( widget.get_active()) return
def _on_get_torrent_status(self, status): # Check to see if we got valid data from the core if status is None: return # Update all the label widgets for widget in self.tab_widgets.values(): txt = xml_escape(self.widget_status_as_fstr(widget, status)) if decode_bytes(widget.obj.get_text()) != txt: if 'comment' in widget.status_keys and is_url(txt): widget.obj.set_markup('<a href="%s">%s</a>' % (txt, txt)) else: widget.obj.set_markup(txt)
def add_torrents(self): (model, row) = self.listview_torrents.get_selection().get_selected() if row is not None: self.save_torrent_options(row) torrents_to_add = [] row = self.torrent_liststore.get_iter_first() while row is not None: torrent_id = self.torrent_liststore.get_value(row, 0) filename = xml_unescape( decode_bytes(self.torrent_liststore.get_value(row, 2))) try: options = self.options[torrent_id] except KeyError: options = None file_priorities = self.get_file_priorities(torrent_id) if options is not None: options['file_priorities'] = file_priorities if self.infos[torrent_id]: torrents_to_add.append(( os.path.split(filename)[-1], b64encode(self.infos[torrent_id]), options, )) elif is_magnet(filename): client.core.add_torrent_magnet(filename, options).addErrback(log.debug) row = self.torrent_liststore.iter_next(row) def on_torrents_added(errors): if errors: log.info( 'Failed to add %d out of %d torrents.', len(errors), len(torrents_to_add), ) for e in errors: log.info('Torrent add failed: %s', e) else: log.info('Successfully added %d torrents.', len(torrents_to_add)) if torrents_to_add: client.core.add_torrent_files(torrents_to_add).addCallback( on_torrents_added)
def on_button_hash_clicked(self, widget): log.debug('on_button_hash_clicked') dialog = self.builder.get_object('dialog_infohash') entry = self.builder.get_object('entry_hash') textview = self.builder.get_object('text_trackers') dialog.set_default_response(Gtk.ResponseType.OK) dialog.set_transient_for(self.dialog) entry.grab_focus() text = get_clipboard_text() if is_infohash(text): entry.set_text(text) dialog.show_all() response = dialog.run() infohash = decode_bytes(entry.get_text()).strip() if response == Gtk.ResponseType.OK and is_infohash(infohash): # Create a list of trackers from the textview buffer tview_buf = textview.get_buffer() trackers_text = decode_bytes( tview_buf.get_text(*tview_buf.get_bounds(), include_hidden_chars=False)) log.debug('Create torrent tracker lines: %s', trackers_text) trackers = list(trackers_tiers_from_text(trackers_text).keys()) # Convert the information to a magnet uri, this is just easier to # handle this way. log.debug('trackers: %s', trackers) magnet = create_magnet_uri(infohash, infohash, trackers) log.debug('magnet uri: %s', magnet) self.add_from_magnets([magnet]) entry.set_text('') textview.get_buffer().set_text('') dialog.hide()
def is_valid(self): """Determines whether file is valid for this reader""" blocklist = self.open() valid = True for line in blocklist: line = decode_bytes(line) if not self.is_ignored(line): try: (start, end) = self.parse(line) if not re.match(r'^(\d{1,3}\.){4}$', start + '.') or not re.match( r'^(\d{1,3}\.){4}$', end + '.'): valid = False except Exception: valid = False break blocklist.close() return valid
def create_column_state(self, column, position=None): if not position: # Find the position for index, c in enumerate(self.treeview.get_columns()): if column.get_title() == c.get_title(): position = index break sort = None if self.model_filter: sort_id, order = self.model_filter.get_sort_column_id() col_title = decode_bytes(column.get_title()) if self.get_column_name(sort_id) == col_title: sort = sort_id return ListViewColumnState( column.get_title(), position, column.get_width(), column.get_visible(), sort, int(column.get_sort_order()), )
def on_button_url_clicked(self, widget): log.debug('on_button_url_clicked') dialog = self.builder.get_object('url_dialog') entry = self.builder.get_object('entry_url') dialog.set_default_response(Gtk.ResponseType.OK) dialog.set_transient_for(self.dialog) entry.grab_focus() text = get_clipboard_text() if text and is_url(text) or is_magnet(text): entry.set_text(text) dialog.show_all() response = dialog.run() if response == Gtk.ResponseType.OK: url = decode_bytes(entry.get_text()) else: url = None entry.set_text('') dialog.hide() # This is where we need to fetch the .torrent file from the URL and # add it to the list. log.debug('url: %s', url) if url: if is_url(url): self.add_from_url(url) elif is_magnet(url): self.add_from_magnets([url]) else: ErrorDialog( _('Invalid URL'), '%s %s' % (url, _('is not a valid URL.')), self.dialog, ).run()
def update_view(self, load_new_list=False): """Update the torrent view model with data we've received.""" filter_column = self.columns['filter'].column_indices[0] status = self.status if not load_new_list: # Freeze notications while updating self.treeview.freeze_child_notify() # Get the columns to update from one of the torrents if status: torrent_id = list(status)[0] fields_to_update = [] for column in self.columns_to_update: column_index = self.get_column_index(column) for i, status_field in enumerate( self.columns[column].status_field): # Only use columns that the torrent has in the state if status_field in status[torrent_id]: fields_to_update.append( (column_index[i], status_field)) for row in self.liststore: torrent_id = row[self.columns['torrent_id'].column_indices[0]] # We expect the torrent_id to be in status and prev_status, # as it will be as long as the list isn't changed by the user torrent_id_in_status = False try: torrent_status = status[torrent_id] torrent_id_in_status = True if torrent_status == self.prev_status[torrent_id]: # The status dict is the same, so do nothing to update for this torrent continue except KeyError: pass if not torrent_id_in_status: if row[filter_column] is True: row[filter_column] = False else: if row[filter_column] is False: row[filter_column] = True # Find the fields to update to_update = [] for i, status_field in fields_to_update: row_value = status[torrent_id][status_field] if decode_bytes(row[i]) != row_value: to_update.append(i) to_update.append(row_value) # Update fields in the liststore if to_update: self.liststore.set(row.iter, *to_update) if load_new_list: # Create the model filter. This sets the model for the treeview and enables sorting. self.create_model_filter() else: self.treeview.thaw_child_notify() component.get('MenuBar').update_menu() self.prev_status = status
def __init__(self, filename='', filetree=1, metainfo=None, metadata=None): # Get the torrent metainfo from the torrent file if metadata: self._metainfo_dict = {b'info': bencode.bdecode(metadata)} self._metainfo = bencode.bencode(self._metainfo_dict) else: self._metainfo = metainfo if filename and not self._metainfo: log.debug('Attempting to open %s.', filename) try: with open(filename, 'rb') as _file: self._metainfo = _file.read() except IOError as ex: log.warning('Unable to open %s: %s', filename, ex) return try: self._metainfo_dict = bencode.bdecode(self._metainfo) except bencode.BTFailure as ex: log.warning('Failed to decode %s: %s', filename, ex) return info_dict = self._metainfo_dict[b'info'] self._info_hash = sha(bencode.bencode(info_dict)).hexdigest() # Get encoding from torrent file if available encoding = self._metainfo_dict.get(b'encoding', None) codepage = self._metainfo_dict.get(b'codepage', None) if not encoding: encoding = codepage if codepage else b'UTF-8' # Decode 'name' with encoding unless 'name.utf-8' found. if b'name.utf-8' in info_dict: self._name = decode_bytes(info_dict[b'name.utf-8']) else: if encoding: encoding = encoding.decode() self._name = decode_bytes(info_dict[b'name'], encoding) # Get list of files from torrent info if b'files' in info_dict: paths = {} dirs = {} prefix = self._name if len(info_dict[b'files']) > 1 else '' for index, f in enumerate(info_dict[b'files']): if b'path.utf-8' in f: path = decode_bytes(os.path.join(*f[b'path.utf-8'])) del f[b'path.utf-8'] else: path = decode_bytes(os.path.join(*f[b'path']), encoding) if prefix: path = os.path.join(prefix, path) f[b'path'] = path f[b'index'] = index if b'sha1' in f and len(f[b'sha1']) == 20: f[b'sha1'] = f[b'sha1'].encode(b'hex') if b'ed2k' in f and len(f[b'ed2k']) == 16: f[b'ed2k'] = f['ed2k'].encode(b'hex') if b'filehash' in f and len(f[b'filehash']) == 20: f[b'filehash'] = f[b'filehash'].encode(b'hex') paths[path] = f dirname = os.path.dirname(path) while dirname: dirinfo = dirs.setdefault(dirname, {}) dirinfo[b'length'] = dirinfo.get(b'length', 0) + f[b'length'] dirname = os.path.dirname(dirname) if filetree == 2: def walk(path, item): if item['type'] == 'dir': item.update(dirs[path]) else: item.update(paths[path]) item['download'] = True file_tree = FileTree2(list(paths)) file_tree.walk(walk) else: def walk(path, item): if isinstance(item, dict): return item return [ paths[path][b'index'], paths[path][b'length'], True ] file_tree = FileTree(paths) file_tree.walk(walk) self._files_tree = file_tree.get_tree() else: if filetree == 2: self._files_tree = { 'contents': { self._name: { 'type': 'file', 'index': 0, 'length': info_dict[b'length'], 'download': True, } } } else: self._files_tree = { self._name: (0, info_dict[b'length'], True) } self._files = [] if b'files' in info_dict: prefix = '' if len(info_dict[b'files']) > 1: prefix = self._name for f in info_dict[b'files']: self._files.append({ 'path': f[b'path'], 'size': f[b'length'], 'download': True }) else: self._files.append({ 'path': self._name, 'size': info_dict[b'length'], 'download': True })
def __init__(self, filename, filetree=1): # Get the torrent data from the torrent file try: log.debug('Attempting to open %s.', filename) with open(filename, 'rb') as _file: self.__m_filedata = _file.read() except IOError as ex: log.warning('Unable to open %s: %s', filename, ex) raise ex try: self.__m_metadata = bencode.bdecode(self.__m_filedata) except bencode.BTFailure as ex: log.warning('Failed to decode %s: %s', filename, ex) raise ex self.__m_info_hash = sha(bencode.bencode(self.__m_metadata['info'])).hexdigest() # Get encoding from torrent file if available self.encoding = None if 'encoding' in self.__m_metadata: self.encoding = self.__m_metadata['encoding'] elif 'codepage' in self.__m_metadata: self.encoding = str(self.__m_metadata['codepage']) if not self.encoding: self.encoding = 'UTF-8' # Check if 'name.utf-8' is in the torrent and if not try to decode the string # using the encoding found. if 'name.utf-8' in self.__m_metadata['info']: self.__m_name = decode_bytes(self.__m_metadata['info']['name.utf-8']) else: self.__m_name = decode_bytes(self.__m_metadata['info']['name'], self.encoding) # Get list of files from torrent info paths = {} dirs = {} if 'files' in self.__m_metadata['info']: prefix = '' if len(self.__m_metadata['info']['files']) > 1: prefix = self.__m_name for index, f in enumerate(self.__m_metadata['info']['files']): if 'path.utf-8' in f: path = decode_bytes(os.path.join(prefix, *f['path.utf-8'])) del f['path.utf-8'] else: path = os.path.join(prefix, decode_bytes(os.path.join(*f['path']), self.encoding)) f['path'] = path f['index'] = index if 'sha1' in f and len(f['sha1']) == 20: f['sha1'] = f['sha1'].encode('hex') if 'ed2k' in f and len(f['ed2k']) == 16: f['ed2k'] = f['ed2k'].encode('hex') if 'filehash' in f and len(f['filehash']) == 20: f['filehash'] = f['filehash'].encode('hex') paths[path] = f dirname = os.path.dirname(path) while dirname: dirinfo = dirs.setdefault(dirname, {}) dirinfo['length'] = dirinfo.get('length', 0) + f['length'] dirname = os.path.dirname(dirname) if filetree == 2: def walk(path, item): if item['type'] == 'dir': item.update(dirs[path]) else: item.update(paths[path]) item['download'] = True file_tree = FileTree2(list(paths)) file_tree.walk(walk) else: def walk(path, item): if isinstance(item, dict): return item return [paths[path]['index'], paths[path]['length'], True] file_tree = FileTree(paths) file_tree.walk(walk) self.__m_files_tree = file_tree.get_tree() else: if filetree == 2: self.__m_files_tree = { 'contents': { self.__m_name: { 'type': 'file', 'index': 0, 'length': self.__m_metadata['info']['length'], 'download': True } } } else: self.__m_files_tree = { self.__m_name: (0, self.__m_metadata['info']['length'], True) } self.__m_files = [] if 'files' in self.__m_metadata['info']: prefix = '' if len(self.__m_metadata['info']['files']) > 1: prefix = self.__m_name for f in self.__m_metadata['info']['files']: self.__m_files.append({ 'path': f['path'], 'size': f['length'], 'download': True }) else: self.__m_files.append({ 'path': self.__m_name, 'size': self.__m_metadata['info']['length'], 'download': True })
def add_column( self, header, render, col_types, hidden, position, status_field, sortid, text=0, value=0, pixbuf=0, function=None, column_type=None, sort_func=None, tooltip=None, default=True, unique=False, default_sort=False, ): """Adds a column to the ListView""" # Add the column types to liststore_columns column_indices = [] if isinstance(col_types, list): for col_type in col_types: self.liststore_columns.append(col_type) column_indices.append(len(self.liststore_columns) - 1) else: self.liststore_columns.append(col_types) column_indices.append(len(self.liststore_columns) - 1) # Add to the index list so we know the order of the visible columns. if position is not None: self.column_index.insert(position, header) else: self.column_index.append(header) # Create a new column object and add it to the list column = self.TreeviewColumn(header) self.columns[header] = self.ListViewColumn(header, column_indices) self.columns[header].column = column self.columns[header].status_field = status_field self.columns[header].sort_func = sort_func self.columns[header].sort_id = column_indices[sortid] # Store creation details self.columns[header].column_type = column_type self.columns[header].renderer = render self.columns[header].text_index = text self.columns[header].value_index = value self.columns[header].pixbuf_index = pixbuf self.columns[header].data_func = function if unique: self.unique_column_id = column_indices[sortid] if default_sort: self.default_sort_column_id = column_indices[sortid] # Create a new list with the added column self.create_new_liststore() # Happens only on columns added after the torrent list has been loaded if self.model_filter: self.create_model_filter() if column_type is None: return self.update_treeview_column(header) column.set_sort_column_id(self.columns[header].column_indices[sortid]) column.set_clickable(True) column.set_resizable(True) column.set_expand(False) column.set_min_width(20) column.set_reorderable(True) column.set_visible(not hidden) column.connect('button-press-event', self.on_treeview_header_right_clicked) if tooltip: column.get_widget().set_tooltip_markup(tooltip) # Check for loaded state and apply column_in_state = False if self.state is not None: for column_state in self.state: if header == decode_bytes(column_state.name): # We found a loaded state column_in_state = True if column_state.width > 0: column.set_sizing(Gtk.TreeViewColumnSizing.FIXED) column.set_fixed_width(column_state.width) column.set_visible(column_state.visible) position = column_state.position break # Set this column to not visible if its not in the state and # its not supposed to be shown by default if not column_in_state and not default and not hidden: column.set_visible(False) if position is not None: self.treeview.insert_column(column, position) else: self.treeview.append_column(column) # Set hidden in the column self.columns[header].hidden = hidden self.columns[header].column = column # Re-create the menu item because of the new column self.create_checklist_menu() return True
def on_button_save_clicked(self, widget): log.debug('on_button_save_clicked') if len(self.files_treestore) == 0: return # Get the path path = self.files_treestore[0][0].rstrip('\\/') torrent_filename = '%s.torrent' % os.path.split(path)[-1] is_remote = 'network' in self.files_treestore[0][1] if is_remote: # This is a remote path dialog = self.builder.get_object('remote_save_dialog') dialog.set_transient_for(self.dialog) dialog_save_path = self.builder.get_object('entry_save_path') dialog_save_path.set_text(path + '.torrent') response = dialog.run() if response == Gtk.ResponseType.OK: result = dialog_save_path.get_text() else: dialog.hide() return dialog.hide() else: # Setup the filechooserdialog chooser = Gtk.FileChooserDialog( _('Save .torrent file'), self.dialog, Gtk.FileChooserAction.SAVE, buttons=( _('_Cancel'), Gtk.ResponseType.CANCEL, _('_Save'), Gtk.ResponseType.OK, ), ) chooser.set_transient_for(self.dialog) chooser.set_select_multiple(False) chooser.set_property('skip-taskbar-hint', True) # Add .torrent and * file filters file_filter = Gtk.FileFilter() file_filter.set_name(_('Torrent files')) file_filter.add_pattern('*.' + 'torrent') chooser.add_filter(file_filter) file_filter = Gtk.FileFilter() file_filter.set_name(_('All files')) file_filter.add_pattern('*') chooser.add_filter(file_filter) chooser.set_current_name(torrent_filename) # Run the dialog response = chooser.run() if response == Gtk.ResponseType.OK: result = chooser.get_filename() else: chooser.destroy() return chooser.destroy() # Fix up torrent filename if len(result) < 9: result += '.torrent' elif result[-8:] != '.torrent': result += '.torrent' # Get a list of trackers trackers = [] if not len(self.trackers_liststore): tracker = None else: # Create a list of lists [[tier0, ...], [tier1, ...], ...] tier_dict = {} for tier, tracker in self.trackers_liststore: tier_dict.setdefault(tier, []).append(tracker) trackers = [tier_dict[tier] for tier in sorted(tier_dict)] # Get the first tracker in the first tier tracker = trackers[0][0] # Get a list of webseeds textview_buf = self.builder.get_object( 'textview_webseeds').get_buffer() lines = (textview_buf.get_text( *textview_buf.get_bounds(), include_hidden_chars=False).strip().split('\n')) webseeds = [] for line in lines: line = line.replace('\\', '/') # Fix any mistyped urls. if is_url(line): webseeds.append(line) # Get the piece length in bytes combo = self.builder.get_object('combo_piece_size') piece_length = self.parse_piece_size_text( combo.get_model()[combo.get_active()][0]) author = self.builder.get_object('entry_author').get_text() comment = self.builder.get_object('entry_comments').get_text() private = self.builder.get_object('chk_private_flag').get_active() add_to_session = self.builder.get_object( 'chk_add_to_session').get_active() if is_remote: def torrent_created(): self.builder.get_object('progress_dialog').hide() client.deregister_event_handler( 'CreateTorrentProgressEvent', on_create_torrent_progress_event) def on_create_torrent_progress_event(piece_count, num_pieces): self._on_create_torrent_progress(piece_count, num_pieces) if piece_count == num_pieces: from twisted.internet import reactor reactor.callLater(0.5, torrent_created) client.register_event_handler('CreateTorrentProgressEvent', on_create_torrent_progress_event) client.core.create_torrent( decode_bytes(path), tracker, piece_length, comment, decode_bytes(result), webseeds, private, author, trackers, add_to_session, ) else: def hide_progress(result): self.builder.get_object('progress_dialog').hide() deferToThread( self.create_torrent, decode_bytes(path), tracker, piece_length, self._on_create_torrent_progress, comment, decode_bytes(result), webseeds, private, author, trackers, add_to_session, ).addCallback(hide_progress) # Setup progress dialog self.builder.get_object('progress_dialog').set_transient_for( component.get('MainWindow').window) self.builder.get_object('progress_dialog').show_all() self.dialog.destroy()
def __init__(self, filename='', filetree=1, torrent_file=None): self._filedata = None if torrent_file: self._metainfo = torrent_file elif filename: log.debug('Attempting to open %s.', filename) try: with open(filename, 'rb') as _file: self._filedata = _file.read() except IOError as ex: log.warning('Unable to open %s: %s', filename, ex) return try: self._metainfo = bencode.bdecode(self._filedata) except bencode.BTFailure as ex: log.warning('Failed to decode %s: %s', filename, ex) return else: log.warning('Requires valid arguments.') return # info_dict with keys decoded to unicode. info_dict = {k.decode(): v for k, v in self._metainfo[b'info'].items()} self._info_hash = sha(bencode.bencode(info_dict)).hexdigest() # Get encoding from torrent file if available encoding = info_dict.get('encoding', None) codepage = info_dict.get('codepage', None) if not encoding: encoding = codepage if codepage else b'UTF-8' if encoding: encoding = encoding.decode() # Decode 'name' with encoding unless 'name.utf-8' found. if 'name.utf-8' in info_dict: self._name = decode_bytes(info_dict['name.utf-8']) else: self._name = decode_bytes(info_dict['name'], encoding) # Get list of files from torrent info self._files = [] if 'files' in info_dict: paths = {} dirs = {} prefix = self._name if len(info_dict['files']) > 1 else '' for index, f in enumerate(info_dict['files']): f = {k.decode(): v for k, v in f.items()} if 'path.utf-8' in f: path = decode_bytes(os.path.join(*f['path.utf-8'])) del f['path.utf-8'] else: path = decode_bytes(os.path.join(*f['path']), encoding) if prefix: path = os.path.join(prefix, path) self._files.append({ 'path': path, 'size': f['length'], 'download': True }) f['path'] = path f['index'] = index if 'sha1' in f and len(f['sha1']) == 20: f['sha1'] = hexlify(f['sha1']).decode() if 'ed2k' in f and len(f['ed2k']) == 16: f['ed2k'] = hexlify(f['ed2k']).decode() if 'filehash' in f and len(f['filehash']) == 20: f['filehash'] = hexlify(f['filehash']).decode() paths[path] = f dirname = os.path.dirname(path) while dirname: dirinfo = dirs.setdefault(dirname, {}) dirinfo['length'] = dirinfo.get('length', 0) + f['length'] dirname = os.path.dirname(dirname) if filetree == 2: def walk(path, item): if item['type'] == 'dir': item.update(dirs[path]) else: item.update(paths[path]) item['download'] = True file_tree = FileTree2(list(paths)) file_tree.walk(walk) else: def walk(path, item): if isinstance(item, dict): return item return [paths[path]['index'], paths[path]['length'], True] file_tree = FileTree(paths) file_tree.walk(walk) self._files_tree = file_tree.get_tree() else: self._files.append({ 'path': self._name, 'size': info_dict['length'], 'download': True }) if filetree == 2: self._files_tree = { 'contents': { self._name: { 'type': 'file', 'index': 0, 'length': info_dict['length'], 'download': True, } } } else: self._files_tree = {self._name: (0, info_dict['length'], True)}