def apply_settings(self, _=None):
        host = "<unknown>"
        port = ""
        try:
            section = "DEFAULT"
            # attempt manual connection
            host = self.hostname_w.get_edit_text()
            port = self.port_w.get_edit_text()
            user = self.username_w.get_edit_text()
            password = self.password_w.get_edit_text()
            if host:
                self.client.connect(
                    host=f"{host}{f':{port}' if port else ''}",
                    username=user,
                    password=password,
                )
                # if successful, save off manual connection information
                config.set(section=section, option="HOST", value=host)
                config.set(section=section, option="PORT", value=port)
                config.set(section=section, option="USERNAME", value=user)
                config.set(section=section, option="PASSWORD", value=password)
            else:
                # find selected pre-defined connection
                for b in self.button_group:
                    if b.get_state():
                        section = b.label
                        break
                # attempt pre-defined connection
                host = config.get(section=section, option="HOST")
                port = config.get(section=section, option="PORT")
                user = config.get(section=section, option="USERNAME")
                password = config.get(section=section, option="PASSWORD")
                self.client.connect(
                    host=f"{host}{f':{port}' if port else ''}",
                    username=user,
                    password=password,
                    verify_certificate=not bool(
                        config.get("DO_NOT_VERIFY_WEBUI_CERTIFICATE")),
                )

            config.set_default_section(section)
            # switch to torrent list window
            reset_daemons.send("connect dialog")
            self.main.app_window.body = self.main.app_window.torrent_list_w
            self.main.loop.widget = self.main.app_window
            initialize_torrent_list.send("connect dialog")
        except LoginFailed:
            self.error_w.set_text(
                f"Error: login failed for {host}{f':{port}' if port else ''}")
        except ConnectorError as e:
            self.error_w.set_text("Error: %s" % e)
Exemple #2
0
    def run(self):
        # start workers
        for worker in self.workers:
            worker.start()

        # TODO: check if any workers died and restart them...maybe
        while not self._stop_request.is_set():
            try:
                self._wake_up.clear()
                self._process_connection_status_notification()
            except Exception:
                pass
            finally:
                self._wake_up.wait(
                    timeout=int(config.get("DAEMON_LOOP_INTERVAL")))

        logger.info("Background manager received stop request")

        # request workers to stop
        for worker in self.workers:
            worker.stop("shutdown")
            worker.join(timeout=1)

        self.signal_ui("daemon manager", "close_pipe")
        os.close(self._daemon_signal_fd)
Exemple #3
0
 def _process_connection_status_notification(self):
     while not self._connection_status_q.empty():
         notification = self._connection_status_q.get()
         sender = notification["sender"]
         success = notification["success"]
         if success is True:
             # if a connection was successful but a previous connection error was reported,
             # tell the UI the connection was reacquired
             if self._connection_status.connection_failure_reported:
                 self.signal_ui(sender="daemon manager",
                                signal="connection_acquired")
             self._time_of_connection_failure = None
             self._connection_status.connection_failure_reported = False
             self._connection_status.never_connected = False
         else:
             if self._connection_status.never_connected is False:
                 if self._time_of_connection_failure is None:
                     self._time_of_connection_failure = time()
                 else:
                     # if a connection has been lost for a little while, report it to the UI
                     if time() - self._time_of_connection_failure > int(
                             config.get(
                                 "TIME_AFTER_CONNECTION_FAILURE_THAT_CONNECTION_IS_CONSIDERED_LOST"
                             )):
                         if (self._connection_status.
                                 connection_failure_reported is False):
                             self.signal_ui(sender="daemon manager",
                                            signal="connection_lost")
                             self._connection_status.connection_failure_reported = (
                                 True)
class AppTitleBar(uw.Text):
    def __init__(self):
        """Application title bar."""
        super(AppTitleBar, self).__init__(markup=APPLICATION_NAME,
                                          align=uw.CENTER,
                                          wrap=uw.CLIP,
                                          layout=None)
        self.refresh("title bar init")
        server_details_changed.connect(receiver=self.refresh)

    def refresh(self, sender, details: dict = None):
        start_time = time()

        div_ch = " | "
        server_version_str = ""
        hostname_str = ""
        title = ""

        if details is None:
            details = {}

        if ver := details.get("server_version", ""):
            server_version_str = ver

        hostname = config.get("HOST")
        port = config.get("PORT")
        hostname_str = (
            f"{hostname if hostname else ''}{f':{port}' if hostname and port else ''}"
        )

        if server_version_str:
            title = server_version_str
        if APPLICATION_NAME:
            title = title + (div_ch if title else "") + APPLICATION_NAME
        if hostname_str:
            title = title + (div_ch if title else "") + hostname_str

        self.set_text(title)

        assert log_timing(logger, "Updating", self, sender, start_time)
 def swap_pb_text_for_pb_bar(self):
     for i, w in enumerate(self.torrent_row_columns_w.base_widget.contents):
         if hasattr(w[0], "name"):
             if w[0].name == "pb_text":
                 self.torrent_row_columns_w.base_widget.contents[i] = (
                     self.torrent_row_columns_w.base_widget.pb_w,
                     self.torrent_row_columns_w.base_widget.options(
                         uw.GIVEN,
                         int(config.get(
                             "TORRENT_LIST_PROGRESS_BAR_LENGTH")),
                         False,
                     ),
                 )
Exemple #6
0
    def __init__(self, torrent_client: Connector):
        super(Daemon, self).__init__()
        self.setDaemon(daemonic=True)
        self.setName(self.__class__.__name__)
        self.stop_request = threading.Event()
        self.wake_up = threading.Event()
        self.reset = threading.Event()

        self._loop_interval = int(config.get("DAEMON_LOOP_INTERVAL"))
        self._loop_success = False

        self.client = torrent_client

        reset_daemons.connect(receiver=self.reset_signal)
    def __init__(self,
                 main,
                 error_message: str = "",
                 support_auto_connect=False):
        self.main = main
        self.client = main.torrent_client

        self.button_group = list()
        self.attempt_auto_connect = False
        for section in config.keys():
            if section != "DEFAULT":
                # if CONNECT_AUTOMATICALLY is set to anything other than
                # 0 or FALSE/false/False, automatically connecting is enabled
                settings_auto_connect = config.get(
                    section=section, option="CONNECT_AUTOMATICALLY")
                is_auto_connect = bool(
                    settings_auto_connect
                    and not settings_auto_connect.upper() == "FALSE"
                    and not settings_auto_connect == "0")
                if (support_auto_connect and is_auto_connect
                        and not self.attempt_auto_connect):
                    uw.RadioButton(self.button_group, section, state=True)
                    self.attempt_auto_connect = True
                else:
                    uw.RadioButton(self.button_group, section, state=False)

        self.error_w = uw.Text(f"{error_message}", align=uw.CENTER)
        self.hostname_w = uw.Edit(" Hostname: ", edit_text="")
        self.port_w = uw.Edit(" Port: ")
        self.username_w = uw.Edit(" Username: "******" Password: "******"*")

        walker_list = [
            uw.Text("Enter connection information", align=uw.CENTER),
            uw.Divider(),
            uw.AttrMap(self.error_w, "light red on default"),
            uw.Divider(),
        ]
        walker_list.extend(self.button_group)
        walker_list.extend([
            uw.Divider(),
            uw.Text("Manual connection:"),
            self.hostname_w,
            self.port_w,
            self.username_w,
            self.password_w,
            uw.Divider(),
            # uw.Divider(),
            uw.Columns(
                [
                    uw.Padding(uw.Text("")),
                    (
                        6,
                        uw.AttrMap(
                            ButtonWithoutCursor("OK",
                                                on_press=self.apply_settings),
                            "",
                            focus_map="selected",
                        ),
                    ),
                    (
                        10,
                        uw.AttrMap(
                            ButtonWithoutCursor("Cancel",
                                                on_press=self.close_dialog),
                            "",
                            focus_map="selected",
                        ),
                    ),
                    uw.Padding(uw.Text("")),
                ],
                dividechars=3,
            ),
            uw.Divider(),
            uw.Divider(),
        ])

        super(ConnectDialog,
              self).__init__(uw.SimpleFocusListWalker(walker_list))

        if self.attempt_auto_connect:
            self.main.loop.set_alarm_in(0.001, callback=self.auto_connect)
 def format_title(v):
     return str(v).ljust(
         int(config.get("TORRENT_LIST_MAX_TORRENT_NAME_LENGTH")))
    def __init__(self):
        self.wide = False

        val_cont = TorrentRowColumns.TorrentInfoColumnValueContainer
        pb_cont = TorrentRowColumns.TorrentInfoColumnPBContainer

        def format_title(v):
            return str(v).ljust(
                int(config.get("TORRENT_LIST_MAX_TORRENT_NAME_LENGTH")))

        self.name_w = val_cont(name="name",
                               raw_value="",
                               format_func=format_title)

        def format_state(v):
            return STATE_MAP_FOR_DISPLAY.get(v, v).ljust(12)

        self.state_w = val_cont(name="state",
                                raw_value="",
                                format_func=format_state)

        def format_size(v):
            return natural_file_size(v, gnu=True).rjust(6)

        self.size_w = val_cont(name="size",
                               raw_value=0,
                               format_func=format_size)

        def format_pb(v: DownloadProgressBar):
            return v.get_percentage().rjust(4)

        self.pb_w = pb_cont(name="pb", current=0)
        self.pb_text_w = val_cont(name="pb_text",
                                  raw_value=self.pb_w,
                                  format_func=format_pb)

        def format_dl_speed(v):
            return natural_file_size(v, gnu=True).rjust(6) + DOWN_TRIANGLE

        self.dl_speed_w = val_cont(name="dlspeed",
                                   raw_value=0,
                                   format_func=format_dl_speed)

        def format_up_speed(v):
            return natural_file_size(v, gnu=True).rjust(6) + UP_TRIANGLE

        self.up_speed_w = val_cont(name="upspeed",
                                   raw_value=0,
                                   format_func=format_up_speed)

        def format_amt_uploaded(v):
            return natural_file_size(v, gnu=True).rjust(6) + UP_ARROW

        self.amt_uploaded_w = val_cont(name="uploaded",
                                       raw_value=0,
                                       format_func=format_amt_uploaded)

        def format_ratio(v):
            return f"R {v:.2f}"

        self.ratio_w = val_cont(name="ratio",
                                raw_value=0,
                                format_func=format_ratio)

        def format_leech_num(v):
            return f"L {v:3d}"

        self.leech_num_w = val_cont(name="num_leechs",
                                    raw_value=0,
                                    format_func=format_leech_num)

        def format_seed_num(v):
            return f"S {v:3d}"

        self.seed_num_w = val_cont(name="num_seeds",
                                   raw_value=0,
                                   format_func=format_seed_num)

        def format_eta(v):
            eta = pretty_time_delta(
                seconds=v) if v < SECS_INFINITY else INFINITY
            # just use first unit from pretty time delta
            with suppress(StopIteration):
                eta = eta[:next(
                    i for i, c in enumerate(eta) if not c.isnumeric()) + 1]
            return f"ETA {eta.rjust(3)}"[:7]

        self.eta_w = val_cont(name="eta",
                              raw_value=SECS_INFINITY,
                              format_func=format_eta)

        def format_category(v):
            return str(v)

        self.category_w = val_cont(name="category",
                                   raw_value="",
                                   format_func=format_category)

        self.pb_info_list = [
            # state
            (len(self.state_w), self.state_w),
            # size
            (len(self.size_w), self.size_w),
            # progress percentage
            (int(config.get("TORRENT_LIST_PROGRESS_BAR_LENGTH")), self.pb_w),
            # dl speed
            (len(self.dl_speed_w), self.dl_speed_w),
            # up speed
            (len(self.up_speed_w), self.up_speed_w),
            # amount uploaded
            (len(self.amt_uploaded_w), self.amt_uploaded_w),
            # share ratio
            (len(self.ratio_w), self.ratio_w),
            # seeders
            (len(self.seed_num_w), self.seed_num_w),
            # leechers
            (len(self.leech_num_w), self.leech_num_w),
            # ETA
            (len(self.eta_w), self.eta_w),
        ]

        self.pb_full_info_list = [(len(self.name_w), self.name_w)]
        self.pb_full_info_list.extend(self.pb_info_list)
        self.pb_full_info_list.append(self.category_w)

        self.text_pb_info_list = list(self.pb_full_info_list)
        self.text_pb_info_list.pop(3)
        self.text_pb_info_list.insert(3, (len(self.pb_text_w), self.pb_text_w))

        super(TorrentRowColumns, self).__init__(
            self.pb_full_info_list,
            dividechars=1,
            focus_column=None,
            min_width=1,
            box_columns=None,
        )
    def resize(self):
        """
        Resize all torrent rows to screen width.

        1) Determine longest torrent name
        2) Resize all torrent names to max name length
        3) Determine widths of different sizings
        4) Apply largest sizing that fits
        """
        # torrent info width with graphic progress bar: 115

        name_list = [
            torrent_row_w.base_widget.cached_torrent["name"]
            for torrent_row_w in self.torrent_row_store.values()
        ]
        if name_list:
            max_name_len = min(
                int(config.get("TORRENT_LIST_MAX_TORRENT_NAME_LENGTH")),
                max(map(len, name_list)),
            )
            for torrent_row_w in self.torrent_row_store.values():
                torrent_row_w.base_widget.resize_name_len(max_name_len)
        else:
            max_name_len = 50

        if self.torrent_list_box_w.width < (max_name_len + 80):
            for torrent_row_w in self.torrent_row_store.values():
                # resize torrent name to 0 (effectively hiding it)
                #  name keeps resetting each time info is updated
                torrent_row_w.base_widget.resize_name_len(0)
                if torrent_row_w.base_widget.current_sizing != "narrow":
                    # logger.info("Resizing %s to narrow" % torrent_row_w.base_widget.cached_torrent.name)
                    # ensure we're using the pb text
                    torrent_row_w.base_widget.swap_pb_bar_for_pb_text()
                    # insert a blank space
                    torrent_row_w.base_widget.torrent_row_columns_w.base_widget.contents.insert(
                        0,
                        (
                            TorrentRowColumns.TorrentInfoColumnValueContainer(
                                name="blank", raw_value=" ", format_func=str),
                            torrent_row_w.base_widget.torrent_row_columns_w.
                            base_widget.options(uw.PACK, None, False),
                        ),
                    )
                    # add the torrent name as a new widget in the Pile for the TorrentRow
                    torrent_row_w.base_widget.contents.insert(
                        0,
                        (
                            uw.Padding(
                                uw.Text(torrent_row_w.base_widget.
                                        cached_torrent["name"])),
                            ("pack", None),
                        ),
                    )
                    torrent_row_w.base_widget.current_sizing = "narrow"

        elif self.torrent_list_box_w.width < (max_name_len + 115):
            for torrent_row_w in self.torrent_row_store.values():
                if torrent_row_w.base_widget.current_sizing != "pb_text":
                    if torrent_row_w.base_widget.current_sizing == "narrow":
                        torrent_row_w.base_widget.torrent_row_columns_w.base_widget.contents.pop(
                            0)
                        torrent_row_w.base_widget.contents.pop(0)
                    # logger.info("Resizing %s to pb text" % torrent_row_w.base_widget.cached_torrent.name)
                    torrent_row_w.base_widget.swap_pb_bar_for_pb_text()
                    torrent_row_w.base_widget.base_widget.current_sizing = "pb_text"

        else:
            for torrent_row_w in self.torrent_row_store.values():
                if torrent_row_w.base_widget.current_sizing != "pb_bar":
                    if torrent_row_w.base_widget.current_sizing == "narrow":
                        torrent_row_w.base_widget.torrent_row_columns_w.base_widget.contents.pop(
                            0)
                        torrent_row_w.base_widget.contents.pop(0)
                    # logger.info("Resizing %s to pb bar" % torrent_row_w.base_widget.cached_torrent.name)
                    torrent_row_w.base_widget.swap_pb_text_for_pb_bar()
                    torrent_row_w.base_widget.current_sizing = "pb_bar"