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
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
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
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
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
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
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
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], }, }
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
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
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
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
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
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
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