ui-mirror-skill 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.
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ download_screenshot.py — Download a screenshot URL or decode a base64 data URL to a local file.
4
+
5
+ Usage:
6
+ python3 download_screenshot.py <url> <output_path>
7
+ python3 download_screenshot.py --url <url> --output <output_path>
8
+
9
+ Handles:
10
+ - HTTP/HTTPS URLs: fetched via urllib.request
11
+ - data: URLs (data:image/png;base64,...): decoded from base64
12
+
13
+ Dependencies: Python 3 standard library only.
14
+ """
15
+
16
+ import base64
17
+ import os
18
+ import sys
19
+ import urllib.request
20
+ import urllib.error
21
+
22
+
23
+ TIMEOUT = 15 # seconds
24
+ MAX_RETRIES = 1
25
+
26
+
27
+ def download_url(url: str, output_path: str) -> None:
28
+ """Download a URL to a local file with retry."""
29
+ os.makedirs(os.path.dirname(os.path.abspath(output_path)), exist_ok=True)
30
+
31
+ last_error = None
32
+ for attempt in range(1 + MAX_RETRIES):
33
+ try:
34
+ req = urllib.request.Request(
35
+ url,
36
+ headers={"User-Agent": "ui-mirror-screenshot-downloader/1.0"},
37
+ )
38
+ with urllib.request.urlopen(req, timeout=TIMEOUT) as resp:
39
+ data = resp.read()
40
+ with open(output_path, "wb") as f:
41
+ f.write(data)
42
+ return
43
+ except (urllib.error.URLError, urllib.error.HTTPError, OSError) as e:
44
+ last_error = e
45
+ if attempt < MAX_RETRIES:
46
+ print(
47
+ "Retry {}/{}: {}".format(attempt + 1, MAX_RETRIES, e),
48
+ file=sys.stderr,
49
+ )
50
+
51
+ raise last_error # type: ignore[misc]
52
+
53
+
54
+ def decode_data_url(data_url: str, output_path: str) -> None:
55
+ """Decode a base64 data URL and write to file."""
56
+ os.makedirs(os.path.dirname(os.path.abspath(output_path)), exist_ok=True)
57
+
58
+ # Parse: data:[<mediatype>][;base64],<data>
59
+ if not data_url.startswith("data:"):
60
+ raise ValueError("Not a data URL: {}".format(data_url[:50]))
61
+
62
+ header_end = data_url.index(",")
63
+ header = data_url[5:header_end] # skip "data:"
64
+ encoded = data_url[header_end + 1:]
65
+
66
+ if ";base64" not in header:
67
+ raise ValueError("Only base64 data URLs are supported, got: {}".format(header))
68
+
69
+ raw = base64.b64decode(encoded)
70
+ with open(output_path, "wb") as f:
71
+ f.write(raw)
72
+
73
+
74
+ def main():
75
+ # Parse args: positional or --url/--output flags
76
+ url = None
77
+ output_path = None
78
+
79
+ args = sys.argv[1:]
80
+ i = 0
81
+ positional = []
82
+ while i < len(args):
83
+ if args[i] == "--url" and i + 1 < len(args):
84
+ url = args[i + 1]
85
+ i += 2
86
+ elif args[i] == "--output" and i + 1 < len(args):
87
+ output_path = args[i + 1]
88
+ i += 2
89
+ elif not args[i].startswith("--"):
90
+ positional.append(args[i])
91
+ i += 1
92
+ else:
93
+ i += 1
94
+
95
+ if url is None and len(positional) >= 1:
96
+ url = positional[0]
97
+ if output_path is None and len(positional) >= 2:
98
+ output_path = positional[1]
99
+
100
+ if not url or not output_path:
101
+ print(
102
+ "Usage: {} <url> <output_path>".format(sys.argv[0]),
103
+ file=sys.stderr,
104
+ )
105
+ print(
106
+ " {} --url <url> --output <output_path>".format(sys.argv[0]),
107
+ file=sys.stderr,
108
+ )
109
+ sys.exit(1)
110
+
111
+ try:
112
+ if url.startswith("data:"):
113
+ decode_data_url(url, output_path)
114
+ print("Decoded data URL -> {}".format(output_path))
115
+ else:
116
+ download_url(url, output_path)
117
+ size = os.path.getsize(output_path)
118
+ print("Downloaded {} -> {} ({} bytes)".format(url, output_path, size))
119
+ except Exception as e:
120
+ print("Error: {}".format(e), file=sys.stderr)
121
+ sys.exit(1)
122
+
123
+
124
+ if __name__ == "__main__":
125
+ main()