Ejemplo n.º 1
0
Archivo: cli.py Proyecto: ygnid/aria2p
def subcommand_resume(api: API,
                      gids: List[str] = None,
                      do_all: bool = False) -> int:
    """
    Resume subcommand.

    Arguments:
        api: The API instance to use.
        gids: The GIDs of the downloads to resume.
        do_all: Pause all downloads if True.

    Returns:
        int: 0 if all success, 1 if one failure.
    """
    if do_all:
        if api.resume_all():
            return 0
        return 1

    try:
        downloads = api.get_downloads(gids)
    except ClientException as error:
        print(str(error), file=sys.stderr)
        return 1

    result = api.resume(downloads)

    if all(result):
        return 0

    for item in result:
        if isinstance(item, ClientException):
            print(item, file=sys.stderr)

    return 1
Ejemplo n.º 2
0
def remove(api: API, gids: List[str] = None, do_all: bool = False, force: bool = False) -> int:
    """
    Remove subcommand.

    Arguments:
        api: The API instance to use.
        gids: The GIDs of the downloads to remove.
        do_all: Pause all downloads if True.
        force: Force pause or not (see API.remove).

    Returns:
        int: 0 if all success, 1 if one failure.
    """
    if do_all:
        if api.remove_all():
            return 0
        return 1

    try:
        downloads = api.get_downloads(gids)
    except ClientException as error:
        print(str(error), file=sys.stderr)
        return 1

    ok = True
    result = api.remove(downloads, force=force)

    if all(result):
        return 0 if ok else 1

    for item in result:
        if isinstance(item, ClientException):
            print(item, file=sys.stderr)

    return 1
Ejemplo n.º 3
0
def listen(
    api: API,
    callbacks_module: PathOrStr = None,
    event_types: List[str] = None,
    timeout: int = 5,
) -> int:
    """
    Listen subcommand.

    Arguments:
        api: The API instance to use.
        callbacks_module: The path to the module to import, containing the callbacks as functions.
        event_types: The event types to process.
        timeout: The timeout to pass to the WebSocket connection, in seconds.

    Returns:
        int: Always 0.
    """
    if not callbacks_module:
        print("aria2p: listen: Please provide the callback module file path with -c option", file=sys.stderr)
        return 1

    if isinstance(callbacks_module, Path):
        callbacks_module = str(callbacks_module)

    if not event_types:
        event_types = ["start", "pause", "stop", "error", "complete", "btcomplete"]

    spec = importlib_util.spec_from_file_location("aria2p_callbacks", callbacks_module)

    if spec is None:
        print(f"aria2p: Could not import module file {callbacks_module}", file=sys.stderr)
        return 1

    callbacks = importlib_util.module_from_spec(spec)

    if callbacks is None:
        print(f"aria2p: Could not import module file {callbacks_module}", file=sys.stderr)
        return 1

    spec.loader.exec_module(callbacks)  # type: ignore

    callbacks_kwargs = {}
    for callback_name in (  # noqa: WPS352 (multiline loop)
        "on_download_start",
        "on_download_pause",
        "on_download_stop",
        "on_download_error",
        "on_download_complete",
        "on_bt_download_complete",
    ):
        if callback_name[3:].replace("download", "").replace("_", "") in event_types:
            callback = getattr(callbacks, callback_name, None)
            if callback:
                callbacks_kwargs[callback_name] = callback

    api.listen_to_notifications(timeout=timeout, handle_signals=True, threaded=False, **callbacks_kwargs)
    return 0
Ejemplo n.º 4
0
Archivo: cli.py Proyecto: ygnid/aria2p
def subcommand_add_metalinks(api: API,
                             metalink_files: List[str] = None,
                             from_file: str = None) -> int:
    """
    Add metalink subcommand.

    Arguments:
        api: The API instance to use.
        metalink_files: The paths to the metalink files.
        from_file: Path to the file to metalink files paths from.

    Returns:
        int: 0 if OK else 1.
    """
    ok = True

    if not metalink_files:
        metalink_files = []

    if from_file:
        try:
            metalink_files.extend(read_lines(from_file))
        except OSError:
            print(f"Cannot open file: {from_file}", file=sys.stderr)
            ok = False

    for metalink_file in metalink_files:
        new_downloads = api.add_metalink(metalink_file)
        for download in new_downloads:
            print(f"Created download {download.gid}")

    return 0 if ok else 1
Ejemplo n.º 5
0
Archivo: cli.py Proyecto: ygnid/aria2p
def subcommand_add_magnets(api: API,
                           uris: List[str] = None,
                           from_file: str = None) -> int:
    """
    Add magnet subcommand.

    Arguments:
        api: The API instance to use.
        uris: The URIs of the magnets.
        from_file: Path to the file to read uris from.

    Returns:
        int: Always 0.
    """
    ok = True

    if not uris:
        uris = []

    if from_file:
        try:
            uris.extend(read_lines(from_file))
        except OSError:
            print(f"Cannot open file: {from_file}", file=sys.stderr)
            ok = False

    for uri in uris:
        new_download = api.add_magnet(uri)
        print(f"Created download {new_download.gid}")

    return 0 if ok else 1
Ejemplo n.º 6
0
Archivo: cli.py Proyecto: ygnid/aria2p
def subcommand_add_torrents(api: API,
                            torrent_files: List[str] = None,
                            from_file: str = None) -> int:
    """
    Add torrent subcommand.

    Arguments:
        api: The API instance to use.
        torrent_files: The paths to the torrent files.
        from_file: Path to the file to read torrent files paths from.

    Returns:
        int: Always 0.
    """
    ok = True

    if not torrent_files:
        torrent_files = []

    if from_file:
        try:
            torrent_files.extend(read_lines(from_file))
        except OSError:
            print(f"Cannot open file: {from_file}", file=sys.stderr)
            ok = False

    for torrent_file in torrent_files:
        new_download = api.add_torrent(torrent_file)
        print(f"Created download {new_download.gid}")

    return 0 if ok else 1
Ejemplo n.º 7
0
Archivo: cli.py Proyecto: ygnid/aria2p
def subcommand_show(api: API) -> int:
    """
    Show subcommand.

    Arguments:
        api: The API instance to use.

    Returns:
        int: Always 0.
    """
    downloads = api.get_downloads()

    def print_line(*args):  # noqa: WPS430 (nested function)
        print("{:<17} {:<9} {:>8} {:>12} {:>12} {:>8}  {}".format(
            *args))  # noqa: P101 (unindexed params)

    print_line("GID", "STATUS", "PROGRESS", "DOWN_SPEED", "UP_SPEED", "ETA",
               "NAME")

    for download in downloads:
        print_line(
            download.gid,
            download.status,
            download.progress_string(),
            download.download_speed_string(),
            download.upload_speed_string(),
            download.eta_string(),
            download.name,
        )

    return 0
Ejemplo n.º 8
0
    def __init__(self, api=None):
        """
        Initialize the object.

        Arguments:
            api (API): An instance of API.
        """
        if api is None:
            api = API()
        self.api = api

        # reduce curses' 1 second delay when hitting escape to 25 ms
        os.environ.setdefault("ESCDELAY", "25")

        self.state_mapping = {
            self.State.MAIN: {
                "process_keyboard_event": self.process_keyboard_event_main,
                "process_mouse_event": self.process_mouse_event_main,
                "print_functions": [self.print_table],
            },
            self.State.HELP: {
                "process_keyboard_event": self.process_keyboard_event_help,
                "process_mouse_event": self.process_mouse_event_help,
                "print_functions": [self.print_help],
            },
            self.State.SETUP: {
                "process_keyboard_event": self.process_keyboard_event_setup,
                "process_mouse_event": self.process_mouse_event_setup,
                "print_functions": [],
            },
            self.State.REMOVE_ASK: {
                "process_keyboard_event":
                self.process_keyboard_event_remove_ask,
                "process_mouse_event":
                self.process_mouse_event_remove_ask,
                "print_functions":
                [self.print_remove_ask_column, self.print_table],
            },
            self.State.SELECT_SORT: {
                "process_keyboard_event":
                self.process_keyboard_event_select_sort,
                "process_mouse_event":
                self.process_mouse_event_select_sort,
                "print_functions":
                [self.print_select_sort_column, self.print_table],
            },
            self.State.ADD_DOWNLOADS: {
                "process_keyboard_event":
                self.process_keyboard_event_add_downloads,
                "process_mouse_event": self.process_mouse_event_add_downloads,
                "print_functions":
                [self.print_add_downloads, self.print_table],
            },
        }
Ejemplo n.º 9
0
Archivo: cli.py Proyecto: ygnid/aria2p
def subcommand_purge(api: API) -> int:
    """
    Purge subcommand.

    Arguments:
        api: The API instance to use.

    Returns:
        int: 0 if all success, 1 if one failure.
    """
    if api.autopurge():
        return 0
    return 1
Ejemplo n.º 10
0
def add(
    api: API,
    uris: List[str] = None,
    from_file: str = None,
    options: dict = None,
    position: int = None,
) -> int:
    """
    Add magnet subcommand.

    Arguments:
        api: The API instance to use.
        uris: The URIs or file-paths to add.
        from_file: Path to the file to read uris from.
            Deprecated: Every URI that is a valid file-path
            and is not a torrent or a metalink is now read as an input file.
        options: String of aria2c options to add to download.
        position: Position to add new download in the queue.

    Returns:
        int: 0 if OK else 1.
    """
    uris = uris or []

    if from_file:
        logger.warning(
            "Deprecation warning: every URI that is a valid file-path "
            "and is not a torrent or a metalink is now read as an input file.",
        )

    new_downloads = []

    for uri in uris:
        created_downloads = api.add(uri, options=options, position=position)
        new_downloads.extend(created_downloads)
        if position is not None:
            position += len(created_downloads)

    if new_downloads:
        for new_download in new_downloads:
            print(f"Created download {new_download.gid}")
        return 0

    print("No new download was created", file=sys.stderr)
    return 1
Ejemplo n.º 11
0
def add_metalinks(
    api: API,
    metalink_files: List[str] = None,
    from_file: str = None,
    options: dict = None,
    position: int = None,
) -> int:
    """
    Add metalink subcommand.

    Arguments:
        api: The API instance to use.
        metalink_files: The paths to the metalink files.
        from_file: Path to the file to metalink files paths from.
        options: String of aria2c options to add to download.
        position: Position to add new download in the queue.

    Returns:
        int: 0 if OK else 1.
    """
    ok = True

    if not metalink_files:
        metalink_files = []

    if from_file:
        try:
            metalink_files.extend(read_lines(from_file))
        except OSError:
            print(f"Cannot open file: {from_file}", file=sys.stderr)
            ok = False

    for metalink_file in metalink_files:
        new_downloads = api.add_metalink(metalink_file,
                                         options=options,
                                         position=position)
        for download in new_downloads:
            print(f"Created download {download.gid}")

    return 0 if ok else 1
Ejemplo n.º 12
0
def add_torrents(
    api: API,
    torrent_files: List[str] = None,
    from_file: str = None,
    options: dict = None,
    position: int = None,
) -> int:
    """
    Add torrent subcommand.

    Arguments:
        api: The API instance to use.
        torrent_files: The paths to the torrent files.
        from_file: Path to the file to read torrent files paths from.
        options: String of aria2c options to add to download.
        position: Position to add new download in the queue.

    Returns:
        int: Always 0.
    """
    ok = True

    if not torrent_files:
        torrent_files = []

    if from_file:
        try:
            torrent_files.extend(read_lines(from_file))
        except OSError:
            print(f"Cannot open file: {from_file}", file=sys.stderr)
            ok = False

    for torrent_file in torrent_files:
        new_download = api.add_torrent(torrent_file,
                                       options=options,
                                       position=position)
        print(f"Created download {new_download.gid}")

    return 0 if ok else 1
Ejemplo n.º 13
0
Archivo: cli.py Proyecto: ygnid/aria2p
def subcommand_add(api: API,
                   uris: List[str] = None,
                   from_file: str = None) -> int:
    """
    Add magnet subcommand.

    Arguments:
        api: The API instance to use.
        uris: The URIs or file-paths to add.
        from_file: Path to the file to read uris from.
            Deprecated: Every URI that is a valid file-path
            and is not a torrent or a metalink is now read as an input file.

    Returns:
        int: 0 if OK else 1.
    """
    uris = uris or []

    if from_file:
        logger.warning(
            "Deprecation warning: every URI that is a valid file-path "
            "and is not a torrent or a metalink is now read as an input file.",
        )

    new_downloads = []

    for uri in uris:
        new_downloads.extend(api.add(uri))

    if new_downloads:
        for new_download in new_downloads:
            print(f"Created download {new_download.gid}")
        return 0

    print("No new download was created", file=sys.stderr)
    return 1
Ejemplo n.º 14
0
def main(args: Optional[List[str]] = None) -> int:
    """
    Run the main program.

    This function is executed when you type `aria2p` or `python -m aria2p`.

    Arguments:
        args: Arguments passed from the command line.

    Returns:
        An exit code.
    """
    parser = get_parser()
    opts = parser.parse_args(args=args)
    kwargs = opts.__dict__  # noqa: WPS609 (special attribute)

    log_level = kwargs.pop("log_level")
    log_path = kwargs.pop("log_path")

    if log_path:
        log_path = Path(log_path)
        if log_path.is_dir():
            log_path = log_path / "aria2p-{time}.log"
        enable_logger(sink=log_path, level=log_level or "WARNING")
    elif log_level:
        enable_logger(sink=sys.stderr, level=log_level)

    logger.debug("Checking arguments")
    check_args(parser, opts)

    logger.debug("Instantiating API")
    api = API(
        Client(
            host=kwargs.pop("host"),
            port=kwargs.pop("port"),
            secret=kwargs.pop("secret"),
            timeout=kwargs.pop("client_timeout"),
        ),
    )

    logger.info(f"API instantiated: {api!r}")

    # Warn if no aria2 daemon process seems to be running
    logger.debug("Testing connection")
    try:
        api.client.get_version()
    except requests.ConnectionError as error:
        print(f"[ERROR] {error}", file=sys.stderr)
        print(file=sys.stderr)
        print("Please make sure that an instance of aria2c is running with RPC mode enabled,", file=sys.stderr)
        print("and that you have provided the right host, port and secret token.", file=sys.stderr)
        print("More information at https://pawamoy.github.io/aria2p.", file=sys.stderr)
        return 2

    subcommand = kwargs.pop("subcommand")

    if subcommand:
        logger.debug("Running subcommand " + subcommand)
    try:
        return commands[subcommand](api, **kwargs)  # type: ignore
    except ClientException as error:  # noqa: WPS440 (variable overlap)
        print(str(error), file=sys.stderr)
        return error.code
Ejemplo n.º 15
0
Archivo: cli.py Proyecto: ygnid/aria2p
def main(args: Optional[List[str]] = None) -> int:
    """
    Run the main program.

    This function is executed when you type `aria2p` or `python -m aria2p`.

    Arguments:
        args: Arguments passed from the command line.

    Returns:
        An exit code.
    """
    parser = get_parser()
    opts = parser.parse_args(args=args)
    kwargs = opts.__dict__  # noqa: WPS609 (special attribute)

    log_level = kwargs.pop("log_level")
    log_path = kwargs.pop("log_path")

    if log_path:
        log_path = Path(log_path)
        if log_path.is_dir():
            log_path = log_path / "aria2p-{time}.log"
        enable_logger(sink=log_path, level=log_level or "WARNING")
    elif log_level:
        enable_logger(sink=sys.stderr, level=log_level)

    logger.debug("Checking arguments")
    check_args(parser, opts)

    logger.debug("Instantiating API")
    api = API(
        Client(
            host=kwargs.pop("host"),
            port=kwargs.pop("port"),
            secret=kwargs.pop("secret"),
            timeout=kwargs.pop("client_timeout"),
        ), )

    logger.info(f"API instantiated: {api!r}")

    # Warn if no aria2 daemon process seems to be running
    logger.debug("Testing connection")
    try:
        api.client.get_version()
    except requests.ConnectionError as error:
        print(f"[ERROR] {error}", file=sys.stderr)
        print(file=sys.stderr)
        print(
            "Please make sure that an instance of aria2c is running with RPC mode enabled,",
            file=sys.stderr)
        print(
            "and that you have provided the right host, port and secret token.",
            file=sys.stderr)
        print("More information at https://aria2p.readthedocs.io/en/latest.",
              file=sys.stderr)
        return 2

    subcommands = {
        None: subcommand_top,
        "show": subcommand_show,
        "top": subcommand_top,
        "call": subcommand_call,
        "add": subcommand_add,
        "add-magnets": subcommand_add_magnets,
        "add-torrents": subcommand_add_torrents,
        "add-metalinks": subcommand_add_metalinks,
        "pause": subcommand_pause,
        "stop": subcommand_pause,  # alias for pause
        "resume": subcommand_resume,
        "start": subcommand_resume,  # alias for resume
        "remove": subcommand_remove,
        "rm": subcommand_remove,  # alias for remove
        "del": subcommand_remove,  # alias for remove
        "delete": subcommand_remove,  # alias for remove
        "purge": subcommand_purge,
        "autopurge": subcommand_purge,  # alias for purge
        "autoclear": subcommand_purge,  # alias for purge
        "autoremove": subcommand_purge,  # alias for purge
        "listen": subcommand_listen,
    }

    subcommand = kwargs.pop("subcommand")

    if subcommand:
        logger.debug("Running subcommand " + subcommand)
    try:
        return subcommands[subcommand](api, **kwargs)
    except ClientException as error:  # noqa: WPS440 (variable overlap)
        print(str(error), file=sys.stderr)
        return error.code