Exemple #1
0
    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]
Exemple #2
0
    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)
Exemple #3
0
    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)
Exemple #4
0
    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))
Exemple #6
0
    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()
Exemple #7
0
    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))
Exemple #8
0
 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()
Exemple #9
0
 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()
Exemple #10
0
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
Exemple #12
0
    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
Exemple #13
0
    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
Exemple #14
0
    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)
Exemple #15
0
    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)
Exemple #16
0
    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()
Exemple #17
0
 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
Exemple #18
0
    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()),
        )
Exemple #19
0
    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()
Exemple #20
0
    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
Exemple #21
0
    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
            })
Exemple #22
0
    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
            })
Exemple #23
0
    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
Exemple #24
0
    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()
Exemple #25
0
    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)}