mountlet 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mountlet/__init__.py +5 -0
- mountlet/cli.py +116 -0
- mountlet/config_tools/export_config.py +98 -0
- mountlet/config_tools/import_config.py +164 -0
- mountlet/config_tools/path_config.py +69 -0
- mountlet/config_tools/reconnect_config.py +66 -0
- mountlet/config_tools/setup_wizard.py +225 -0
- mountlet/config_tools/shared.py +289 -0
- mountlet/config_tools/verify_config.py +91 -0
- mountlet/core.py +532 -0
- mountlet/settings.py +237 -0
- mountlet/tray.py +732 -0
- mountlet/tui.py +200 -0
- mountlet-0.2.0.dist-info/METADATA +231 -0
- mountlet-0.2.0.dist-info/RECORD +19 -0
- mountlet-0.2.0.dist-info/WHEEL +5 -0
- mountlet-0.2.0.dist-info/entry_points.txt +2 -0
- mountlet-0.2.0.dist-info/licenses/LICENSE +21 -0
- mountlet-0.2.0.dist-info/top_level.txt +1 -0
mountlet/__init__.py
ADDED
mountlet/cli.py
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
from collections.abc import Callable
|
|
7
|
+
|
|
8
|
+
from . import __version__
|
|
9
|
+
from . import tui
|
|
10
|
+
from .config_tools import export_config, import_config, path_config, reconnect_config, setup_wizard, verify_config
|
|
11
|
+
|
|
12
|
+
Command = Callable[[list[str] | None], int | None]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def run_menu(argv: list[str] | None = None) -> int:
|
|
16
|
+
if argv:
|
|
17
|
+
print("The menu command does not accept options.", file=sys.stderr)
|
|
18
|
+
return 2
|
|
19
|
+
if not setup_wizard.ensure_ready_for_menu():
|
|
20
|
+
return 1
|
|
21
|
+
result = tui.main()
|
|
22
|
+
return int(result or 0)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def run_path(argv: list[str] | None = None) -> int:
|
|
26
|
+
return int(path_config.main(argv) or 0)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def run_setup(argv: list[str] | None = None) -> int:
|
|
30
|
+
return int(setup_wizard.main(argv) or 0)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def run_verify(argv: list[str] | None = None) -> int:
|
|
34
|
+
return int(verify_config.main(argv) or 0)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def run_reconnect(argv: list[str] | None = None) -> int:
|
|
38
|
+
return int(reconnect_config.main(argv) or 0)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def run_export(argv: list[str] | None = None) -> int:
|
|
42
|
+
return int(export_config.main(argv) or 0)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def run_import(argv: list[str] | None = None) -> int:
|
|
46
|
+
return int(import_config.main(argv) or 0)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def run_tray(argv: list[str] | None = None) -> int:
|
|
50
|
+
from . import tray
|
|
51
|
+
|
|
52
|
+
return int(tray.main(argv) or 0)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
COMMANDS: dict[str, tuple[str, Command]] = {
|
|
56
|
+
"menu": ("Open the interactive mount menu.", run_menu),
|
|
57
|
+
"tray": ("Open the desktop tray app.", run_tray),
|
|
58
|
+
"setup": ("Prepare the app for first use.", run_setup),
|
|
59
|
+
"path": ("Show config, state, cache, and rclone paths.", run_path),
|
|
60
|
+
"verify": ("Check whether configured remotes are reachable.", run_verify),
|
|
61
|
+
"reconnect": ("Refresh credentials for one or more remotes.", run_reconnect),
|
|
62
|
+
"export": ("Export an rclone configuration backup bundle.", run_export),
|
|
63
|
+
"import": ("Import an rclone configuration backup bundle.", run_import),
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
ALIASES = {
|
|
67
|
+
"paths": "path",
|
|
68
|
+
"tui": "menu",
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def print_help() -> None:
|
|
73
|
+
print("mountlet")
|
|
74
|
+
print()
|
|
75
|
+
print("Usage:")
|
|
76
|
+
print(" mountlet")
|
|
77
|
+
print(" mountlet <command> [options]")
|
|
78
|
+
print()
|
|
79
|
+
print("Commands:")
|
|
80
|
+
for name, (description, _) in COMMANDS.items():
|
|
81
|
+
print(f" {name:<10} {description}")
|
|
82
|
+
print()
|
|
83
|
+
print("Run 'mountlet <command> --help' for command-specific options.")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def main(argv: list[str] | None = None) -> int:
|
|
87
|
+
args = list(sys.argv[1:] if argv is None else argv)
|
|
88
|
+
if args and args[0] in {"--version", "-V"}:
|
|
89
|
+
print(f"mountlet {__version__}")
|
|
90
|
+
return 0
|
|
91
|
+
if not args:
|
|
92
|
+
return run_menu()
|
|
93
|
+
|
|
94
|
+
command_name = args.pop(0)
|
|
95
|
+
if command_name in {"-h", "--help", "help"}:
|
|
96
|
+
if command_name == "help" and args:
|
|
97
|
+
command_name = ALIASES.get(args[0], args[0])
|
|
98
|
+
command = COMMANDS.get(command_name)
|
|
99
|
+
if command:
|
|
100
|
+
return int(command[1](["--help"]) or 0)
|
|
101
|
+
print_help()
|
|
102
|
+
return 0
|
|
103
|
+
|
|
104
|
+
command_name = ALIASES.get(command_name, command_name)
|
|
105
|
+
command = COMMANDS.get(command_name)
|
|
106
|
+
if not command:
|
|
107
|
+
print(f"[!] Unknown command: {command_name}", file=sys.stderr)
|
|
108
|
+
print_help()
|
|
109
|
+
return 2
|
|
110
|
+
|
|
111
|
+
result = command[1](args)
|
|
112
|
+
return int(result or 0)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
if __name__ == "__main__":
|
|
116
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
"""Export the current rclone configuration and secrets into a directory."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import argparse
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from .shared import (
|
|
11
|
+
copy_file,
|
|
12
|
+
default_config_path,
|
|
13
|
+
find_client_secrets,
|
|
14
|
+
ensure_dir,
|
|
15
|
+
print_remote_list,
|
|
16
|
+
read_remotes,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def export_bundle(args: argparse.Namespace) -> int:
|
|
21
|
+
source_conf = Path(args.config).expanduser().resolve() if args.config else default_config_path()
|
|
22
|
+
if not source_conf.exists():
|
|
23
|
+
print(f"[!] Config file not found: {source_conf}")
|
|
24
|
+
return 1
|
|
25
|
+
|
|
26
|
+
destination = Path(args.destination).expanduser().resolve()
|
|
27
|
+
ensure_dir(destination)
|
|
28
|
+
|
|
29
|
+
copied_conf = copy_file(source_conf, destination / source_conf.name, backup=args.backup, dry_run=args.dry_run)
|
|
30
|
+
if copied_conf:
|
|
31
|
+
action = "would copy" if args.dry_run else "copied"
|
|
32
|
+
print(f"[*] {action} {source_conf} -> {copied_conf}")
|
|
33
|
+
|
|
34
|
+
secrets: list[Path] = []
|
|
35
|
+
for secret in args.client_secret:
|
|
36
|
+
path = Path(secret).expanduser().resolve()
|
|
37
|
+
if path.exists():
|
|
38
|
+
secrets.append(path)
|
|
39
|
+
else:
|
|
40
|
+
print(f"[!] client secret missing: {path}")
|
|
41
|
+
|
|
42
|
+
if args.auto_discover_secrets:
|
|
43
|
+
for item in find_client_secrets(source_conf.parent):
|
|
44
|
+
if item not in secrets:
|
|
45
|
+
secrets.append(item)
|
|
46
|
+
|
|
47
|
+
for secret in secrets:
|
|
48
|
+
dest_secret = destination / secret.name
|
|
49
|
+
copied_secret = copy_file(secret, dest_secret, backup=args.backup, dry_run=args.dry_run)
|
|
50
|
+
if copied_secret:
|
|
51
|
+
action = "would copy" if args.dry_run else "copied"
|
|
52
|
+
print(f"[*] {action} {secret} -> {copied_secret}")
|
|
53
|
+
|
|
54
|
+
if not args.dry_run:
|
|
55
|
+
remotes = read_remotes(source_conf)
|
|
56
|
+
print_remote_list(remotes, source_conf)
|
|
57
|
+
print(f"[i] Bundle ready at {destination}")
|
|
58
|
+
return 0
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
62
|
+
parser = argparse.ArgumentParser(description="Export rclone configuration and secrets into a directory.")
|
|
63
|
+
parser.add_argument("destination", help="Directory where the bundle will be written.")
|
|
64
|
+
parser.add_argument(
|
|
65
|
+
"--config",
|
|
66
|
+
help="Override source rclone.conf (default: detected path).",
|
|
67
|
+
)
|
|
68
|
+
parser.add_argument(
|
|
69
|
+
"--client-secret",
|
|
70
|
+
action="append",
|
|
71
|
+
default=[],
|
|
72
|
+
help="Explicit client_secret JSON files to include (repeatable).",
|
|
73
|
+
)
|
|
74
|
+
parser.add_argument(
|
|
75
|
+
"--no-auto-discover",
|
|
76
|
+
dest="auto_discover_secrets",
|
|
77
|
+
action="store_false",
|
|
78
|
+
help="Disable searching for client_secret*.json next to the config file.",
|
|
79
|
+
)
|
|
80
|
+
parser.add_argument(
|
|
81
|
+
"--no-backup",
|
|
82
|
+
dest="backup",
|
|
83
|
+
action="store_false",
|
|
84
|
+
help="Do not backup existing files in destination.",
|
|
85
|
+
)
|
|
86
|
+
parser.add_argument("--dry-run", action="store_true", help="Preview without copying.")
|
|
87
|
+
parser.set_defaults(auto_discover_secrets=True, backup=True)
|
|
88
|
+
return parser
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def main(argv: list[str] | None = None) -> int:
|
|
92
|
+
parser = build_parser()
|
|
93
|
+
args = parser.parse_args(argv)
|
|
94
|
+
return export_bundle(args)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
if __name__ == "__main__":
|
|
98
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
"""Import an rclone configuration bundle into the active environment."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import argparse
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from .shared import (
|
|
11
|
+
copy_file,
|
|
12
|
+
default_config_path,
|
|
13
|
+
ensure_dir,
|
|
14
|
+
find_client_secrets,
|
|
15
|
+
print_remote_list,
|
|
16
|
+
read_remotes,
|
|
17
|
+
run_rclone_version,
|
|
18
|
+
verify_remotes,
|
|
19
|
+
reconnect_remotes,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def import_bundle(args: argparse.Namespace) -> int:
|
|
24
|
+
target_conf = default_config_path()
|
|
25
|
+
target_dir = target_conf.parent
|
|
26
|
+
|
|
27
|
+
if args.target_dir:
|
|
28
|
+
target_dir = Path(args.target_dir).expanduser().resolve()
|
|
29
|
+
target_conf = target_dir / target_conf.name
|
|
30
|
+
|
|
31
|
+
ensure_dir(target_dir)
|
|
32
|
+
|
|
33
|
+
src_config = Path(args.config).expanduser().resolve()
|
|
34
|
+
if not src_config.exists():
|
|
35
|
+
print(f"[!] Config file not found: {src_config}")
|
|
36
|
+
return 1
|
|
37
|
+
|
|
38
|
+
copied_conf = copy_file(src_config, target_conf, backup=args.backup, dry_run=args.dry_run)
|
|
39
|
+
if copied_conf:
|
|
40
|
+
action = "would copy" if args.dry_run else "copied"
|
|
41
|
+
print(f"[*] {action} {src_config} -> {copied_conf}")
|
|
42
|
+
|
|
43
|
+
secrets: list[Path] = []
|
|
44
|
+
for secret in args.client_secret:
|
|
45
|
+
secret_path = Path(secret).expanduser().resolve()
|
|
46
|
+
if secret_path.exists():
|
|
47
|
+
secrets.append(secret_path)
|
|
48
|
+
else:
|
|
49
|
+
print(f"[!] client secret missing: {secret_path}")
|
|
50
|
+
|
|
51
|
+
if args.auto_discover_secrets:
|
|
52
|
+
for item in find_client_secrets(src_config.parent):
|
|
53
|
+
if item not in secrets:
|
|
54
|
+
secrets.append(item)
|
|
55
|
+
|
|
56
|
+
for secret in secrets:
|
|
57
|
+
dest_secret = target_dir / secret.name
|
|
58
|
+
copied_secret = copy_file(secret, dest_secret, backup=args.backup, dry_run=args.dry_run)
|
|
59
|
+
if copied_secret:
|
|
60
|
+
action = "would copy" if args.dry_run else "copied"
|
|
61
|
+
print(f"[*] {action} {secret} -> {copied_secret}")
|
|
62
|
+
|
|
63
|
+
reference_config = src_config if args.dry_run else target_conf
|
|
64
|
+
remotes = read_remotes(reference_config)
|
|
65
|
+
print_remote_list(remotes, reference_config)
|
|
66
|
+
|
|
67
|
+
if args.dry_run:
|
|
68
|
+
return 0
|
|
69
|
+
|
|
70
|
+
version_info = run_rclone_version()
|
|
71
|
+
print(f"[*] rclone check: {version_info}")
|
|
72
|
+
if not remotes or args.skip_verify:
|
|
73
|
+
if remotes and args.skip_verify:
|
|
74
|
+
print("[i] Verification skipped. Run 'mountlet verify' when ready.")
|
|
75
|
+
elif remotes:
|
|
76
|
+
print("[i] No remotes found to verify.")
|
|
77
|
+
return 0
|
|
78
|
+
|
|
79
|
+
print("[i] Verifying imported remotes...")
|
|
80
|
+
results = verify_remotes(remotes)
|
|
81
|
+
failures = [name for name, (ok, _) in results.items() if not ok]
|
|
82
|
+
|
|
83
|
+
if failures and args.verify_auto_reconnect:
|
|
84
|
+
print("[i] Attempting to reconnect failing remotes...")
|
|
85
|
+
reconnect_remotes(failures, args.reconnect_auto_confirm)
|
|
86
|
+
print_remote_list(failures, reference_config, label="Re-checking remotes after reconnect from")
|
|
87
|
+
results.update(verify_remotes(failures))
|
|
88
|
+
failures = [name for name, (ok, _) in results.items() if not ok]
|
|
89
|
+
|
|
90
|
+
if failures:
|
|
91
|
+
for name in failures:
|
|
92
|
+
print(f"[!] {name}: still failing after import.")
|
|
93
|
+
return 1
|
|
94
|
+
|
|
95
|
+
print("[✓] All imported remotes verified.")
|
|
96
|
+
return 0
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
100
|
+
parser = argparse.ArgumentParser(description="Import an rclone configuration and credentials bundle.")
|
|
101
|
+
parser.add_argument(
|
|
102
|
+
"--config",
|
|
103
|
+
required=True,
|
|
104
|
+
help="Path to the rclone.conf file to import.",
|
|
105
|
+
)
|
|
106
|
+
parser.add_argument(
|
|
107
|
+
"--client-secret",
|
|
108
|
+
action="append",
|
|
109
|
+
default=[],
|
|
110
|
+
help="Path to a client_secret JSON file to import (repeatable).",
|
|
111
|
+
)
|
|
112
|
+
parser.add_argument(
|
|
113
|
+
"--no-auto-discover",
|
|
114
|
+
dest="auto_discover_secrets",
|
|
115
|
+
action="store_false",
|
|
116
|
+
help="Disable searching for client_secret*.json next to the config file.",
|
|
117
|
+
)
|
|
118
|
+
parser.add_argument(
|
|
119
|
+
"--target-dir",
|
|
120
|
+
help="Override destination directory (default: OS rclone directory).",
|
|
121
|
+
)
|
|
122
|
+
parser.add_argument(
|
|
123
|
+
"--no-backup",
|
|
124
|
+
dest="backup",
|
|
125
|
+
action="store_false",
|
|
126
|
+
help="Do not backup existing files.",
|
|
127
|
+
)
|
|
128
|
+
parser.add_argument("--dry-run", action="store_true", help="Preview without copying.")
|
|
129
|
+
parser.add_argument(
|
|
130
|
+
"--no-verify",
|
|
131
|
+
dest="skip_verify",
|
|
132
|
+
action="store_true",
|
|
133
|
+
help="Skip verification after import.",
|
|
134
|
+
)
|
|
135
|
+
parser.add_argument(
|
|
136
|
+
"--verify-auto-reconnect",
|
|
137
|
+
dest="verify_auto_reconnect",
|
|
138
|
+
action="store_true",
|
|
139
|
+
help="After import verification, run reconnect for failing remotes.",
|
|
140
|
+
)
|
|
141
|
+
parser.add_argument(
|
|
142
|
+
"--no-reconnect-auto-confirm",
|
|
143
|
+
dest="reconnect_auto_confirm",
|
|
144
|
+
action="store_false",
|
|
145
|
+
help="When auto-reconnecting, do not pass --auto-confirm to rclone.",
|
|
146
|
+
)
|
|
147
|
+
parser.set_defaults(
|
|
148
|
+
auto_discover_secrets=True,
|
|
149
|
+
backup=True,
|
|
150
|
+
skip_verify=False,
|
|
151
|
+
verify_auto_reconnect=False,
|
|
152
|
+
reconnect_auto_confirm=True,
|
|
153
|
+
)
|
|
154
|
+
return parser
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def main(argv: list[str] | None = None) -> int:
|
|
158
|
+
parser = build_parser()
|
|
159
|
+
args = parser.parse_args(argv)
|
|
160
|
+
return import_bundle(args)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
if __name__ == "__main__":
|
|
164
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
"""Print Mountlet and rclone path locations."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import argparse
|
|
8
|
+
|
|
9
|
+
from .shared import (
|
|
10
|
+
app_cache_dir,
|
|
11
|
+
app_config_dir,
|
|
12
|
+
app_config_file,
|
|
13
|
+
app_mounts_file,
|
|
14
|
+
app_state_dir,
|
|
15
|
+
default_config_path,
|
|
16
|
+
ensure_app_directories,
|
|
17
|
+
)
|
|
18
|
+
from ..settings import ensure_default_config_files
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
22
|
+
parser = argparse.ArgumentParser(description="Print user-specific configuration and data paths.")
|
|
23
|
+
parser.add_argument("--ensure", action="store_true", help="Create Mountlet user directories.")
|
|
24
|
+
parser.add_argument("--rclone-config", action="store_true", help="Print only the rclone config path.")
|
|
25
|
+
parser.add_argument(
|
|
26
|
+
"--app-config",
|
|
27
|
+
action="store_true",
|
|
28
|
+
help="Print only the Mountlet config file path.",
|
|
29
|
+
)
|
|
30
|
+
parser.add_argument("--state", action="store_true", help="Print only the Mountlet state directory.")
|
|
31
|
+
parser.add_argument("--cache", action="store_true", help="Print only the Mountlet cache directory.")
|
|
32
|
+
parser.add_argument(
|
|
33
|
+
"--mounts-config",
|
|
34
|
+
action="store_true",
|
|
35
|
+
help="Print only the per-remote mount config file path.",
|
|
36
|
+
)
|
|
37
|
+
return parser
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def main(argv: list[str] | None = None) -> int:
|
|
41
|
+
args = build_parser().parse_args(argv)
|
|
42
|
+
if args.ensure:
|
|
43
|
+
ensure_app_directories()
|
|
44
|
+
ensure_default_config_files()
|
|
45
|
+
|
|
46
|
+
selected = {
|
|
47
|
+
"rclone_config": args.rclone_config,
|
|
48
|
+
"app_config": args.app_config,
|
|
49
|
+
"mounts_config": args.mounts_config,
|
|
50
|
+
"state": args.state,
|
|
51
|
+
"cache": args.cache,
|
|
52
|
+
}
|
|
53
|
+
show_all = not any(selected.values())
|
|
54
|
+
paths = [
|
|
55
|
+
("rclone config", "rclone_config", default_config_path()),
|
|
56
|
+
("app config", "app_config", app_config_file()),
|
|
57
|
+
("mounts config", "mounts_config", app_mounts_file()),
|
|
58
|
+
("app config directory", "app_config", app_config_dir()),
|
|
59
|
+
("state directory", "state", app_state_dir()),
|
|
60
|
+
("cache directory", "cache", app_cache_dir()),
|
|
61
|
+
]
|
|
62
|
+
for label, key, path in paths:
|
|
63
|
+
if show_all or selected[key]:
|
|
64
|
+
print(f"{label}: {path}")
|
|
65
|
+
return 0
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
if __name__ == "__main__":
|
|
69
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
"""Force rclone to reconnect selected remotes."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import argparse
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from .shared import (
|
|
11
|
+
default_config_path,
|
|
12
|
+
print_remote_list,
|
|
13
|
+
resolve_remote_selection,
|
|
14
|
+
reconnect_remotes,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def reconnect_command(args: argparse.Namespace) -> int:
|
|
19
|
+
config_path = Path(args.config).expanduser().resolve() if args.config else default_config_path()
|
|
20
|
+
if not config_path.exists():
|
|
21
|
+
print(f"[!] Config file not found: {config_path}")
|
|
22
|
+
return 1
|
|
23
|
+
|
|
24
|
+
selected, missing = resolve_remote_selection(config_path, args.remote)
|
|
25
|
+
for name in missing:
|
|
26
|
+
print(f"[!] Remote not found in config: {name}")
|
|
27
|
+
|
|
28
|
+
if not selected:
|
|
29
|
+
print("[!] No remotes available for reconnection.")
|
|
30
|
+
return 1
|
|
31
|
+
|
|
32
|
+
print_remote_list(selected, config_path, label="Reconnecting remotes from")
|
|
33
|
+
reconnect_remotes(selected, args.reconnect_auto_confirm)
|
|
34
|
+
return 0
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
38
|
+
parser = argparse.ArgumentParser(description="Run 'rclone config reconnect' for selected remotes.")
|
|
39
|
+
parser.add_argument(
|
|
40
|
+
"--config",
|
|
41
|
+
help="Config file to read remotes from (default: detected rclone path).",
|
|
42
|
+
)
|
|
43
|
+
parser.add_argument(
|
|
44
|
+
"--remote",
|
|
45
|
+
action="append",
|
|
46
|
+
default=[],
|
|
47
|
+
help="Specific remote to reconnect (repeatable). Defaults to all remotes in the config.",
|
|
48
|
+
)
|
|
49
|
+
parser.add_argument(
|
|
50
|
+
"--no-auto-confirm",
|
|
51
|
+
dest="reconnect_auto_confirm",
|
|
52
|
+
action="store_false",
|
|
53
|
+
help="Do not pass --auto-confirm to reconnect commands.",
|
|
54
|
+
)
|
|
55
|
+
parser.set_defaults(reconnect_auto_confirm=True)
|
|
56
|
+
return parser
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def main(argv: list[str] | None = None) -> int:
|
|
60
|
+
parser = build_parser()
|
|
61
|
+
args = parser.parse_args(argv)
|
|
62
|
+
return reconnect_command(args)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
if __name__ == "__main__":
|
|
66
|
+
raise SystemExit(main())
|