@codebend3r/che 1.0.0

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.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +424 -0
  3. package/bin/che.py +869 -0
  4. package/bin/commands.py +872 -0
  5. package/bin/drives/eject-all-drives.py +203 -0
  6. package/bin/drives/mount-all-drives.py +140 -0
  7. package/bin/drives/ping-nas.py +242 -0
  8. package/bin/files/compress-folders.py +188 -0
  9. package/bin/files/delete-by-ext.py +108 -0
  10. package/bin/files/delete-empty-folders.py +136 -0
  11. package/bin/files/delete-smb-files.py +105 -0
  12. package/bin/files/files-under-size.py +132 -0
  13. package/bin/files/find-largest-files.py +102 -0
  14. package/bin/files/make-alpha-dir.py +63 -0
  15. package/bin/git/all-actions.py +545 -0
  16. package/bin/git/checkout-my-branches.py +147 -0
  17. package/bin/git/clean-stale-branches.py +161 -0
  18. package/bin/git/prune-worktrees.py +143 -0
  19. package/bin/git/sync-all-branches.py +368 -0
  20. package/bin/git/update-local-branches.py +195 -0
  21. package/bin/install.py +1515 -0
  22. package/bin/shellgen.py +782 -0
  23. package/bin/system/btop-launch.py +114 -0
  24. package/bin/system/update-brew.py +146 -0
  25. package/bin/tui.py +648 -0
  26. package/bin/utils.py +836 -0
  27. package/bin/video/delete-duplicate-videos.py +313 -0
  28. package/bin/video/detect-green-magenta-videos.py +268 -0
  29. package/bin/video/find-movie-by-year.py +84 -0
  30. package/bin/video/find-video-mkv-issues.py +242 -0
  31. package/bin/video/fix-codecs.py +209 -0
  32. package/bin/video/largest-tv-shows.py +175 -0
  33. package/bin/video/remove-metadata.py +126 -0
  34. package/bin/video/rename-video-file.py +329 -0
  35. package/bin/video/scan-videos-audio-language.py +110 -0
  36. package/bin/video/show-codecs.py +111 -0
  37. package/bin/video/validate-video-files.py +114 -0
  38. package/bin/video/video-list.py +120 -0
  39. package/package.json +54 -0
  40. package/shell/che.bash +382 -0
  41. package/shell/che.fish +282 -0
  42. package/shell/che.zsh +389 -0
  43. package/shell/completions/_che +146 -0
  44. package/shell/completions/che.bash +107 -0
  45. package/shell/completions/che.fish +371 -0
@@ -0,0 +1,203 @@
1
+ #!/usr/bin/env python3
2
+ """📤 Eject all NAS volumes from /Volumes.
3
+
4
+ Checks the primary mount as well as the duplicate ``<name>-1`` and IP-named
5
+ mounts macOS creates when it re-mounts the same share twice. Stale placeholder
6
+ directories left behind by a force-unmount are cleaned up with rmdir (sudo as a
7
+ fallback).
8
+
9
+ Also resets Finder's Favorite Servers and Recent Hosts lists, otherwise ejected
10
+ NAS shares keep reappearing in the sidebar Locations after Finder relaunches.
11
+
12
+ Usage:
13
+ eject-all-drives.py [--dry-run] [--only=NAME] [--no-force]
14
+ [--no-clear-favorites] [--quiet]
15
+
16
+ Options:
17
+ --dry-run Show what would be unmounted, change nothing
18
+ --only=NAME Only eject this drive (case-insensitive); skips the
19
+ Favorite Servers reset, which would be over-broad
20
+ --no-force Use 'diskutil unmount' instead of 'diskutil unmount force'
21
+ --no-clear-favorites Don't reset Finder's Favorite Servers / Recent Hosts
22
+ --quiet Only log unmounts and failures
23
+ --help Show help
24
+ """
25
+
26
+ from __future__ import annotations
27
+
28
+ import sys
29
+ from pathlib import Path
30
+
31
+ sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
32
+
33
+ from utils import (
34
+ add_bool_flag,
35
+ build_parser,
36
+ info,
37
+ log,
38
+ note,
39
+ require_binary,
40
+ run,
41
+ run_cli,
42
+ success,
43
+ warning,
44
+ )
45
+
46
+ __version__ = "3.3.0"
47
+
48
+ DRIVES: tuple[tuple[str, str], ...] = (
49
+ ("Meleys", "192.168.50.2"),
50
+ ("Vermithor", "192.168.50.3"),
51
+ ("Caraxes", "192.168.50.4"),
52
+ ("Syrax", "192.168.50.5"),
53
+ ("Vhagar", "192.168.50.6"),
54
+ )
55
+
56
+
57
+ def eject_volume(label: str, *, dry_run: bool, force: bool, quiet: bool) -> str:
58
+ """Unmount ``/Volumes/<label>``. Returns "ejected", "skipped" or "failed"."""
59
+ mount = Path("/Volumes") / label
60
+
61
+ if not mount.is_dir():
62
+ if not quiet:
63
+ info(f" ⏭️ {label} — not mounted")
64
+ return "skipped"
65
+
66
+ if dry_run:
67
+ warning(f" 🪄 Would unmount {mount}")
68
+ return "ejected"
69
+
70
+ unmount_cmd = ["diskutil", "unmount"]
71
+ if force:
72
+ unmount_cmd.append("force")
73
+
74
+ log(f" 📤 Unmounting {mount}...")
75
+ completed = run([*unmount_cmd, str(mount)], check=False, capture=True)
76
+ if completed.returncode != 0:
77
+ warning(f" ❌ Failed to unmount {mount}")
78
+ return "failed"
79
+
80
+ # A force-unmount can leave the placeholder directory behind. Try to clear
81
+ # it unprivileged first; fall back to sudo, and give up quietly if neither
82
+ # works, exactly as the shell version did.
83
+ if mount.is_dir():
84
+ try:
85
+ mount.rmdir()
86
+ except OSError:
87
+ run(["sudo", "rmdir", str(mount)], check=False, capture=True)
88
+
89
+ success(f" ✅ {label} ejected")
90
+ return "ejected"
91
+
92
+
93
+ def reset_finder_state(
94
+ selected: list[tuple[str, str]], *, clear_favorites: bool, only: str
95
+ ) -> None:
96
+ """Drop Finder's server-level entries so the sidebar stops resurrecting them."""
97
+ log("🪪 Asking Finder to drop any server-level entries...")
98
+ for name, _ in selected:
99
+ run(
100
+ [
101
+ "osascript",
102
+ "-e",
103
+ 'tell application "Finder" to try',
104
+ "-e",
105
+ f'eject disk "{name}"',
106
+ "-e",
107
+ "end try",
108
+ ],
109
+ check=False,
110
+ capture=True,
111
+ )
112
+
113
+ # Skipped when --only is set: resetlist is all-or-nothing and would nuke
114
+ # the other drives' favorites too.
115
+ if not only and clear_favorites:
116
+ log("🧽 Resetting Finder Favorite Servers + Recent Hosts lists...")
117
+ for list_name in (
118
+ "com.apple.LSSharedFileList.FavoriteServers",
119
+ "com.apple.LSSharedFileList.RecentHosts",
120
+ ):
121
+ run(["sfltool", "resetlist", list_name], check=False, capture=True)
122
+
123
+ # SIGKILL specifically: a graceful quit lets Finder save its current
124
+ # sidebar state on the way out and restore the stale entries on relaunch.
125
+ log("🔄 Hard-killing Finder so it doesn't restore stale sidebar state...")
126
+ run(["killall", "-KILL", "Finder"], check=False, capture=True)
127
+
128
+
129
+ def main(argv: list[str] | None = None) -> int:
130
+ parser = build_parser(
131
+ prog="eject-all-drives",
132
+ description="📤 Eject all NAS volumes from /Volumes.",
133
+ )
134
+ add_bool_flag(parser, "--dry-run", help="Show what would be unmounted, change nothing")
135
+ parser.add_argument(
136
+ "--only", default="", metavar="NAME", help="Only eject this drive (case-insensitive)"
137
+ )
138
+ add_bool_flag(
139
+ parser,
140
+ "--no-force",
141
+ dest="no_force",
142
+ allow_value=False,
143
+ help="Use 'diskutil unmount' instead of 'diskutil unmount force'",
144
+ )
145
+ add_bool_flag(
146
+ parser,
147
+ "--no-clear-favorites",
148
+ dest="no_clear_favorites",
149
+ allow_value=False,
150
+ help="Don't reset Finder's Favorite Servers / Recent Hosts lists",
151
+ )
152
+ add_bool_flag(parser, "--quiet", help="Only log unmounts and failures")
153
+ args = parser.parse_args(argv)
154
+
155
+ force = not args.no_force
156
+ clear_favorites = not args.no_clear_favorites
157
+
158
+ # Only gate on diskutil for a real run: the shell version's dry-run never
159
+ # invoked it, so previewing worked anywhere.
160
+ if not args.dry_run:
161
+ require_binary("diskutil", hint="This script is macOS-only.")
162
+
163
+ header_suffix = " 🌵 (dry run)" if args.dry_run else ""
164
+ note("═══════════════════════════════════════════")
165
+ note(f"📤 eject-all-drives{header_suffix}")
166
+ note("═══════════════════════════════════════════")
167
+
168
+ selected = [(n, ip) for n, ip in DRIVES if not args.only or n.lower() == args.only.lower()]
169
+ if args.only and not selected:
170
+ # Warn, but keep the shell version's exit code and control flow: an
171
+ # unknown --only printed a 0/0 summary and still exited 0.
172
+ warning(f"⚠️ No drive named '{args.only}'. Known: {', '.join(n for n, _ in DRIVES)}")
173
+
174
+ counts = {"ejected": 0, "skipped": 0, "failed": 0}
175
+
176
+ for name, ip in selected:
177
+ info(f"🐉 {name} ({ip})")
178
+ # macOS re-mounts of the same share land at "<name>-1" or at the IP.
179
+ for label in (name, f"{name}-1", ip):
180
+ counts[eject_volume(label, dry_run=args.dry_run, force=force, quiet=args.quiet)] += 1
181
+
182
+ print()
183
+ note("─────────────────────────────────────────────")
184
+
185
+ if args.dry_run:
186
+ success(f"🌵 Would eject: {counts['ejected']}")
187
+ info(f"⏭️ Skipped: {counts['skipped']} (not mounted)")
188
+ log("💡 Dry run complete. Nothing was actually ejected.")
189
+ return 0
190
+
191
+ success(f"✅ Ejected: {counts['ejected']}")
192
+ info(f"⏭️ Skipped: {counts['skipped']} (not mounted)")
193
+ if counts["failed"]:
194
+ warning(f"❌ Failed: {counts['failed']}")
195
+ return 1
196
+
197
+ reset_finder_state(selected, clear_favorites=clear_favorites, only=args.only)
198
+ log("🎉 All clean! 🧹✨")
199
+ return 0
200
+
201
+
202
+ if __name__ == "__main__":
203
+ run_cli(main)
@@ -0,0 +1,140 @@
1
+ #!/usr/bin/env python3
2
+ """💿 Mount all NAS drives over SMB via AppleScript's ``mount volume``.
3
+
4
+ Using AppleScript (rather than ``mount_smbfs``) takes the same NetAuth path
5
+ Finder does, so ``/Volumes/<name>`` is created automatically and Keychain
6
+ credentials are used silently, with no Finder modals. Already-mounted drives
7
+ are skipped. The share name is assumed to match the drive name, e.g.
8
+ ``//crivas@Meleys/Meleys`` → ``/Volumes/Meleys``.
9
+
10
+ Usage:
11
+ mount-all-drives.py [--only=NAME] [--use-ip] [--quiet]
12
+
13
+ Options:
14
+ --only=NAME Only mount the drive with this name (case-insensitive)
15
+ --use-ip Mount via //USER@IP/NAME instead of //USER@NAME/NAME
16
+ --quiet Only log mounts and failures
17
+ --help Show help
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import os
23
+ import sys
24
+ from pathlib import Path
25
+
26
+ sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
27
+
28
+ from utils import (
29
+ add_bool_flag,
30
+ build_parser,
31
+ info,
32
+ log,
33
+ note,
34
+ require_binary,
35
+ run,
36
+ run_cli,
37
+ success,
38
+ warning,
39
+ )
40
+
41
+ __version__ = "4.1.0"
42
+
43
+ SMB_USER = "crivas"
44
+
45
+ # (name, ip) - the share name is assumed to match the drive name.
46
+ DRIVES: tuple[tuple[str, str], ...] = (
47
+ ("Meleys", "192.168.50.2"),
48
+ ("Vermithor", "192.168.50.3"),
49
+ ("Caraxes", "192.168.50.4"),
50
+ ("Syrax", "192.168.50.5"),
51
+ ("Vhagar", "192.168.50.6"),
52
+ )
53
+
54
+
55
+ def is_mounted(name: str) -> bool:
56
+ """True when something is actually mounted at ``/Volumes/<name>``.
57
+
58
+ The shell version grepped ``mount`` output for `` on /Volumes/<name> (``;
59
+ ``os.path.ismount`` asks the kernel directly. The two agree for every
60
+ healthy mount. The one real difference: ``ismount`` lstats the mount root,
61
+ so on a wedged SMB mount it can block for the server timeout where reading
62
+ the mount table cannot.
63
+ """
64
+ return os.path.ismount(f"/Volumes/{name}")
65
+
66
+
67
+ def mount_drive(name: str, host: str, *, quiet: bool) -> str:
68
+ """Mount one drive. Returns "mounted", "skipped" or "failed"."""
69
+ if is_mounted(name):
70
+ if not quiet:
71
+ info(f"⏭️ {name} — already mounted")
72
+ return "skipped"
73
+
74
+ url = f"smb://{SMB_USER}@{host}/{name}"
75
+ log(f"🔗 Mounting {name} ({url})...")
76
+
77
+ completed = run(
78
+ ["osascript", "-e", f'mount volume "{url}"'],
79
+ check=False,
80
+ capture=True,
81
+ )
82
+ if completed.returncode == 0:
83
+ success(f" ✅ {name} mounted 🐉")
84
+ return "mounted"
85
+
86
+ warning(f" ❌ mount failed for {url}")
87
+ stderr = (completed.stderr or "").strip()
88
+ if stderr:
89
+ warning(f" {stderr}")
90
+ warning(f" Check that Keychain has a password for '{SMB_USER}@{host}'.")
91
+ return "failed"
92
+
93
+
94
+ def main(argv: list[str] | None = None) -> int:
95
+ parser = build_parser(
96
+ prog="mount-all-drives",
97
+ description="💿 Mount all NAS drives over SMB via AppleScript.",
98
+ )
99
+ parser.add_argument(
100
+ "--only", default="", metavar="NAME", help="Only mount this drive (case-insensitive)"
101
+ )
102
+ add_bool_flag(parser, "--use-ip", help="Mount via //USER@IP/NAME instead of //USER@NAME/NAME")
103
+ add_bool_flag(parser, "--quiet", help="Only log mounts and failures")
104
+ args = parser.parse_args(argv)
105
+
106
+ require_binary("osascript", hint="This script is macOS-only.")
107
+
108
+ note("═══════════════════════════════════════════")
109
+ note("💿 mount-all-drives")
110
+ note("═══════════════════════════════════════════")
111
+
112
+ counts = {"mounted": 0, "skipped": 0, "failed": 0}
113
+ matched = False
114
+
115
+ for name, ip in DRIVES:
116
+ if args.only and name.lower() != args.only.lower():
117
+ continue
118
+ matched = True
119
+ host = ip if args.use_ip else name
120
+ counts[mount_drive(name, host, quiet=args.quiet)] += 1
121
+
122
+ if args.only and not matched:
123
+ # Warn, but keep the shell version's exit code: an unknown --only
124
+ # printed a 0/0 summary and still exited 0.
125
+ warning(f"⚠️ No drive named '{args.only}'. Known: {', '.join(n for n, _ in DRIVES)}")
126
+
127
+ print()
128
+ note("─────────────────────────────────────────────")
129
+ success(f"✅ Mounted: {counts['mounted']}")
130
+ info(f"⏭️ Skipped: {counts['skipped']} (already mounted)")
131
+ if counts["failed"]:
132
+ warning(f"❌ Failed: {counts['failed']}")
133
+ return 1
134
+
135
+ log("🎉 All drives are up! 🐉🐉🐉")
136
+ return 0
137
+
138
+
139
+ if __name__ == "__main__":
140
+ run_cli(main)
@@ -0,0 +1,242 @@
1
+ #!/usr/bin/env python3
2
+ """📡 Keep-alive pinger for the NAS drives.
3
+
4
+ Each cycle pings every host and verifies it is still mounted under /Volumes.
5
+ If a drive is reachable but the share has dropped off /Volumes (eject, network
6
+ blip, sleep) it is silently remounted via AppleScript's ``mount volume`` using
7
+ Keychain credentials, with no Finder modals.
8
+
9
+ Usage:
10
+ ping-nas.py [--interval=SECONDS] [--ping-timeout=SECONDS] [--only=NAME]
11
+ [--no-remount] [--use-ip] [--once] [--quiet]
12
+
13
+ Options:
14
+ --interval=N Seconds between cycles (default: 300)
15
+ --ping-timeout=N Per-host ping timeout in seconds (default: 2)
16
+ --only=NAME Only ping the drive with this name (case-insensitive)
17
+ --no-remount Detect-only: warn about missing mounts, don't remount
18
+ --use-ip Remount via //USER@IP/NAME instead of //USER@NAME/NAME
19
+ --once Run a single cycle and exit (useful from cron)
20
+ --quiet Only log failures, remount attempts, and cycle summary
21
+ --help Show help
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import os
27
+ import re
28
+ import signal
29
+ import sys
30
+ import time
31
+ from pathlib import Path
32
+ from types import FrameType
33
+
34
+ sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
35
+
36
+ from utils import (
37
+ add_bool_flag,
38
+ build_parser,
39
+ info,
40
+ log,
41
+ note,
42
+ run,
43
+ run_cli,
44
+ success,
45
+ warning,
46
+ )
47
+
48
+ __version__ = "3.3.0"
49
+
50
+ SMB_USER = "crivas"
51
+
52
+ DRIVES: tuple[tuple[str, str], ...] = (
53
+ ("Meleys", "192.168.50.2"),
54
+ ("Vermithor", "192.168.50.3"),
55
+ ("Caraxes", "192.168.50.4"),
56
+ ("Syrax", "192.168.50.5"),
57
+ ("Vhagar", "192.168.50.6"),
58
+ )
59
+
60
+ _cycle = 0
61
+
62
+
63
+ def is_mounted(name: str) -> bool:
64
+ """True when something is actually mounted at ``/Volumes/<name>``."""
65
+ return os.path.ismount(f"/Volumes/{name}")
66
+
67
+
68
+ def ping_host(ip: str, timeout: int) -> bool:
69
+ """One ICMP probe with a per-host timeout. macOS ``ping -t`` is seconds."""
70
+ completed = run(["ping", "-c", "1", "-t", str(timeout), ip], check=False, capture=True)
71
+ return completed.returncode == 0
72
+
73
+
74
+ def remount_drive(name: str, host: str) -> bool:
75
+ """Remount one drive via AppleScript. True on success."""
76
+ url = f"smb://{SMB_USER}@{host}/{name}"
77
+ log(f" 🔗 Remounting {name} ({url})...")
78
+
79
+ completed = run(["osascript", "-e", f'mount volume "{url}"'], check=False, capture=True)
80
+ if completed.returncode == 0:
81
+ success(f" ✅ {name} remounted 🐉")
82
+ return True
83
+
84
+ warning(f" ❌ mount failed for {url}")
85
+ stderr = (completed.stderr or "").strip()
86
+ if stderr:
87
+ warning(f" {stderr}")
88
+ warning(f" Check that Keychain has a password for '{SMB_USER}@{host}'.")
89
+ return False
90
+
91
+
92
+ def ping_all_nas(
93
+ active: list[tuple[str, str]],
94
+ *,
95
+ ping_timeout: int,
96
+ remount: bool,
97
+ use_ip: bool,
98
+ quiet: bool,
99
+ ) -> None:
100
+ """Run one full check cycle over the active drive list."""
101
+ global _cycle
102
+ _cycle += 1
103
+
104
+ unreachable = 0
105
+ unmounted = 0
106
+ remounted = 0
107
+ remount_failed = 0
108
+
109
+ if not quiet:
110
+ log(f"🔄 Cycle #{_cycle} — checking {len(active)} NAS drive(s)...")
111
+
112
+ for name, ip in active:
113
+ if not ping_host(ip, ping_timeout):
114
+ warning(f" ❌ {name} ({ip}) — ping failed")
115
+ unreachable += 1
116
+ continue
117
+
118
+ if is_mounted(name):
119
+ if not quiet:
120
+ log(f" ✅ 🐉 {name} ({ip}) — mounted")
121
+ continue
122
+
123
+ warning(f" ⚠️ {name} ({ip}) — reachable but not mounted")
124
+ unmounted += 1
125
+
126
+ if remount:
127
+ if remount_drive(name, ip if use_ip else name):
128
+ remounted += 1
129
+ else:
130
+ remount_failed += 1
131
+
132
+ if unreachable == 0 and unmounted == 0:
133
+ if not quiet:
134
+ success(f"✨ All {len(active)} drive(s) responded and mounted 🎉")
135
+ return
136
+
137
+ parts = []
138
+ if unreachable:
139
+ parts.append(f"{unreachable} unreachable")
140
+ if remounted:
141
+ parts.append(f"{remounted} remounted")
142
+ if remount_failed:
143
+ parts.append(f"{remount_failed} remount-failed")
144
+ if not remount and unmounted:
145
+ parts.append(f"{unmounted} unmounted")
146
+ warning(f"⚠️ Cycle summary: {' '.join(parts)}")
147
+
148
+
149
+ def _shutdown(signum: int, frame: FrameType | None) -> None:
150
+ """Exit 0 on Ctrl-C, matching the shell version's INT/TERM trap."""
151
+ print()
152
+ info(f"👋 Stopping keep-alive pinger after {_cycle} cycle(s).")
153
+ sys.exit(0)
154
+
155
+
156
+ def main(argv: list[str] | None = None) -> int:
157
+ parser = build_parser(
158
+ prog="ping-nas",
159
+ description="📡 Keep-alive pinger for the NAS drives.",
160
+ epilog=(
161
+ "Each cycle pings every host and verifies it is still mounted under\n"
162
+ "/Volumes. A drive that is reachable but has dropped off /Volumes is\n"
163
+ f"silently remounted via AppleScript as '{SMB_USER}' using Keychain\n"
164
+ "credentials, with no Finder modals."
165
+ ),
166
+ )
167
+ # --interval and --ping-timeout stay strings so the shell version's own
168
+ # message survives; `type=int` would let argparse own the failure first.
169
+ parser.add_argument(
170
+ "--interval", default="300", metavar="N", help="Seconds between cycles (default: 300)"
171
+ )
172
+ parser.add_argument(
173
+ "--ping-timeout",
174
+ dest="ping_timeout",
175
+ default="2",
176
+ metavar="N",
177
+ help="Per-host ping timeout in seconds (default: 2)",
178
+ )
179
+ parser.add_argument(
180
+ "--only", default="", metavar="NAME", help="Only ping this drive (case-insensitive)"
181
+ )
182
+ add_bool_flag(
183
+ parser,
184
+ "--no-remount",
185
+ dest="no_remount",
186
+ allow_value=False,
187
+ help="Detect-only: warn about missing mounts, don't remount",
188
+ )
189
+ add_bool_flag(
190
+ parser,
191
+ "--use-ip",
192
+ dest="use_ip",
193
+ help="Remount via //USER@IP/NAME instead of //USER@NAME/NAME",
194
+ )
195
+ add_bool_flag(parser, "--once", help="Run a single cycle and exit (useful from cron)")
196
+ add_bool_flag(parser, "--quiet", help="Only log failures, remount attempts, and cycle summary")
197
+ args = parser.parse_args(argv)
198
+
199
+ values: dict[str, int] = {}
200
+ for flag, raw in (("--interval", args.interval), ("--ping-timeout", args.ping_timeout)):
201
+ if not re.fullmatch(r"[0-9]+", raw) or int(raw) < 1:
202
+ warning(f"❌ {flag} must be a positive integer (got: {raw})")
203
+ return 1
204
+ values[flag] = int(raw)
205
+ interval = values["--interval"]
206
+ ping_timeout = values["--ping-timeout"]
207
+
208
+ active = [(n, ip) for n, ip in DRIVES if not args.only or n.lower() == args.only.lower()]
209
+ if not active:
210
+ warning(f"❌ No drives matched --only={args.only}")
211
+ return 1
212
+
213
+ signal.signal(signal.SIGINT, _shutdown)
214
+ signal.signal(signal.SIGTERM, _shutdown)
215
+
216
+ note("═══════════════════════════════════════════")
217
+ note("📡 ping-nas")
218
+ note("═══════════════════════════════════════════")
219
+
220
+ cycle_kwargs = {
221
+ "ping_timeout": ping_timeout,
222
+ "remount": not args.no_remount,
223
+ "use_ip": args.use_ip,
224
+ "quiet": args.quiet,
225
+ }
226
+
227
+ if args.once:
228
+ ping_all_nas(active, **cycle_kwargs)
229
+ return 0
230
+
231
+ info(
232
+ f"🚀 Keep-alive pinger started for {len(active)} NAS "
233
+ f"(every {interval}s). Press Ctrl-C to stop."
234
+ )
235
+
236
+ while True:
237
+ ping_all_nas(active, **cycle_kwargs)
238
+ time.sleep(interval)
239
+
240
+
241
+ if __name__ == "__main__":
242
+ run_cli(main)