| Hash | Commit message | Author | Date | Files | + | - |
1 | commit a045a0a935b12794a6b1261c0c08b04fae8fedcc |
2 | Author: Connor Etherington <[email protected]> |
3 | Date: Sat Jul 29 06:02:15 2023 +0200 |
4 | |
5 | Update. |
6 | --- |
7 | ptrack.egg-info/PKG-INFO | 6 + |
8 | ptrack.egg-info/SOURCES.txt | 9 ++ |
9 | ptrack.egg-info/dependency_links.txt | 1 + |
10 | ptrack.egg-info/entry_points.txt | 4 + |
11 | ptrack.egg-info/requires.txt | 3 + |
12 | ptrack.egg-info/top_level.txt | 1 + |
13 | ptrack/__init__.py | 14 +++ |
14 | ptrack/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 731 bytes |
15 | ptrack/__pycache__/main.cpython-310.pyc | Bin 0 -> 5541 bytes |
16 | ptrack/main.py | 184 ++++++++++++++++++++++++++++ |
17 | setup.py | 22 ++++ |
18 | 11 files changed, 244 insertions(+) |
19 | |
20 | diff --git a/ptrack.egg-info/PKG-INFO b/ptrack.egg-info/PKG-INFO |
21 | new file mode 100644 |
22 | index 0000000..7da1525 |
23 | --- /dev/null |
24 | +++ b/ptrack.egg-info/PKG-INFO |
25 | @@ -0,0 +1,6 @@ |
26 | +Metadata-Version: 2.1 |
27 | +Name: ptrack |
28 | +Version: 0.1.0 |
29 | +Summary: A simple CLI utility for asthetically tracking progress when copying or moving files. |
30 | +Author: Connor Etherington |
31 | +Author-email: [email protected] |
32 | diff --git a/ptrack.egg-info/SOURCES.txt b/ptrack.egg-info/SOURCES.txt |
33 | new file mode 100644 |
34 | index 0000000..f85fb63 |
35 | --- /dev/null |
36 | +++ b/ptrack.egg-info/SOURCES.txt |
37 | @@ -0,0 +1,9 @@ |
38 | +setup.py |
39 | +ptrack/__init__.py |
40 | +ptrack/main.py |
41 | +ptrack.egg-info/PKG-INFO |
42 | +ptrack.egg-info/SOURCES.txt |
43 | +ptrack.egg-info/dependency_links.txt |
44 | +ptrack.egg-info/entry_points.txt |
45 | +ptrack.egg-info/requires.txt |
46 | +ptrack.egg-info/top_level.txt |
47 | diff --git a/ptrack.egg-info/dependency_links.txt b/ptrack.egg-info/dependency_links.txt |
48 | new file mode 100644 |
49 | index 0000000..8b13789 |
50 | --- /dev/null |
51 | +++ b/ptrack.egg-info/dependency_links.txt |
52 | @@ -0,0 +1 @@ |
53 | + |
54 | diff --git a/ptrack.egg-info/entry_points.txt b/ptrack.egg-info/entry_points.txt |
55 | new file mode 100644 |
56 | index 0000000..32a28e4 |
57 | --- /dev/null |
58 | +++ b/ptrack.egg-info/entry_points.txt |
59 | @@ -0,0 +1,4 @@ |
60 | +[console_scripts] |
61 | +ptc = ptrack.main:copy |
62 | +ptm = ptrack.main:move |
63 | +ptrack = ptrack.main:main |
64 | diff --git a/ptrack.egg-info/requires.txt b/ptrack.egg-info/requires.txt |
65 | new file mode 100644 |
66 | index 0000000..382c1b3 |
67 | --- /dev/null |
68 | +++ b/ptrack.egg-info/requires.txt |
69 | @@ -0,0 +1,3 @@ |
70 | +rich |
71 | +argparse |
72 | +argcomplete |
73 | diff --git a/ptrack.egg-info/top_level.txt b/ptrack.egg-info/top_level.txt |
74 | new file mode 100644 |
75 | index 0000000..c003217 |
76 | --- /dev/null |
77 | +++ b/ptrack.egg-info/top_level.txt |
78 | @@ -0,0 +1 @@ |
79 | +ptrack |
80 | diff --git a/ptrack/__init__.py b/ptrack/__init__.py |
81 | new file mode 100644 |
82 | index 0000000..919fce7 |
83 | --- /dev/null |
84 | +++ b/ptrack/__init__.py |
85 | @@ -0,0 +1,14 @@ |
86 | +import argparse |
87 | +import argcomplete |
88 | + |
89 | +parser = argparse.ArgumentParser(description='A simple CLI utility for asthetically tracking progress when copying or moving files.') |
90 | +parser.add_argument('-v', '--verbose', action='store_true', help='verbose output') |
91 | +parser.add_argument('-c', '--copy', action='store_true', help='copy files (You can use `ptc` instead of `ptrack -c`)') |
92 | +parser.add_argument('-m', '--move', action='store_true', help='move files (You can use `ptm` instead of `ptrack -m`)') |
93 | + |
94 | +argcomplete.autocomplete(parser) |
95 | +args, unknown = parser.parse_known_args() |
96 | + |
97 | +verbose = args.verbose |
98 | +copy = args.copy |
99 | +move = args.move |
100 | diff --git a/ptrack/__pycache__/__init__.cpython-310.pyc b/ptrack/__pycache__/__init__.cpython-310.pyc |
101 | new file mode 100644 |
102 | index 0000000..d544d60 |
103 | Binary files /dev/null and b/ptrack/__pycache__/__init__.cpython-310.pyc differ |
104 | diff --git a/ptrack/__pycache__/main.cpython-310.pyc b/ptrack/__pycache__/main.cpython-310.pyc |
105 | new file mode 100644 |
106 | index 0000000..c3479bd |
107 | Binary files /dev/null and b/ptrack/__pycache__/main.cpython-310.pyc differ |
108 | diff --git a/ptrack/main.py b/ptrack/main.py |
109 | new file mode 100755 |
110 | index 0000000..bddf173 |
111 | --- /dev/null |
112 | +++ b/ptrack/main.py |
113 | @@ -0,0 +1,184 @@ |
114 | +import os |
115 | +import sys |
116 | +from rich.progress import Progress, BarColumn, TextColumn, TimeRemainingColumn, FileSizeColumn |
117 | +from rich.console import Console |
118 | +import shutil |
119 | +import ptrack |
120 | + |
121 | +verbose = ptrack.verbose |
122 | +argCopy = ptrack.copy |
123 | +argMove = ptrack.move |
124 | + |
125 | + |
126 | +def format_file_size(file_size): |
127 | + if file_size >= 1024 * 1024 * 1024: |
128 | + return f"{file_size / (1024*1024*1024):.2f} GB" |
129 | + elif file_size >= 1024 * 1024: |
130 | + return f"{file_size / (1024*1024):.2f} MB" |
131 | + elif file_size >= 1024: |
132 | + return f"{file_size / 1024:.2f} kB" |
133 | + else: |
134 | + return f"{file_size} bytes" |
135 | + |
136 | + |
137 | +def regular_copy(src, dst, console, task, progress): |
138 | + with open(src, 'rb') as fsrc, open(dst, 'wb') as fdst: |
139 | + while True: |
140 | + buf = fsrc.read(1024*1024) |
141 | + if not buf: |
142 | + break |
143 | + fdst.write(buf) |
144 | + progress.update(task, advance=len(buf)) |
145 | + progress.refresh() |
146 | + |
147 | + |
148 | +def verbose_copy(src, dst, console, current, total_files): |
149 | + |
150 | + file_size = os.path.getsize(src) |
151 | + |
152 | + with Progress( |
153 | + BarColumn(bar_width=50), |
154 | + "[progress.percentage]{task.percentage:>3.0f}%", |
155 | + TimeRemainingColumn(), |
156 | + "[#ea2a6f][[/#ea2a6f]", |
157 | + FileSizeColumn(), |
158 | + "[#ea2a6f]/[/#ea2a6f]", |
159 | + TextColumn(f"[bold cyan]{format_file_size(file_size)}[/bold cyan]"), |
160 | + "[#ea2a6f]][/#ea2a6f]", |
161 | + f"({current} of {total_files})", |
162 | + console=console, |
163 | + auto_refresh=False |
164 | + ) as progress: |
165 | + task = progress.add_task("", total=file_size, file_size=format_file_size(file_size)) |
166 | + |
167 | + with open(src, 'rb') as fsrc, open(dst, 'wb') as fdst: |
168 | + while not progress.finished: |
169 | + buf = fsrc.read(1024*1024) |
170 | + if not buf: |
171 | + break |
172 | + fdst.write(buf) |
173 | + progress.update(task, advance=len(buf)) |
174 | + progress.refresh() |
175 | + |
176 | + |
177 | +def run(process): |
178 | + console = Console() |
179 | + |
180 | + if len(sys.argv) < 3: |
181 | + hlp() |
182 | + if process == "Copying": |
183 | + console.print("[bold cyan]Usage: ptc [OPTIONS] SOURCE... DESTINATION[/bold cyan]") |
184 | + elif process == "Moving": |
185 | + console.print("[bold cyan]Usage: ptm [OPTIONS] SOURCE... DESTINATION[/bold cyan]") |
186 | + sys.exit(1) |
187 | + |
188 | + src_paths = sys.argv[1:-1] |
189 | + dst = sys.argv[-1] |
190 | + srcPaths = [] |
191 | + |
192 | + for path in src_paths: |
193 | + if path.endswith('/'): |
194 | + path = path[:-1] |
195 | + srcPaths.append(path) |
196 | + |
197 | + if os.path.isdir(dst): |
198 | + dst_dir = dst |
199 | + new_name = None |
200 | + else: |
201 | + dst_dir = os.path.dirname(dst) |
202 | + new_name = os.path.basename(dst) |
203 | + |
204 | + total_files = sum(len(files) for path in srcPaths for r, d, files in os.walk(path) if os.path.isdir(path)) + sum(1 for path in srcPaths if os.path.isfile(path)) |
205 | + total_size = sum(os.path.getsize(os.path.join(r, f)) for path in srcPaths for r, d, files in os.walk(path) for f in files) + sum(os.path.getsize(path) for path in srcPaths if os.path.isfile(path)) |
206 | + current_file = 1 |
207 | + |
208 | + if total_files > 1: |
209 | + console.print(f"\n[#ea2a6f]{process}:[/#ea2a6f] [bold cyan]{total_files} files[/bold cyan]\n") |
210 | + else: |
211 | + for src_path in srcPaths: |
212 | + if os.path.isfile(src_path): |
213 | + console.print(f"\n[#ea2a6f]{process}:[/#ea2a6f] [bold cyan] {os.path.basename(src_path)} [/bold cyan]\n") |
214 | + |
215 | + if verbose: |
216 | + for src_path in srcPaths: |
217 | + if os.path.isfile(src_path): |
218 | + dst_path = os.path.join(dst_dir, os.path.basename(src_path) if not new_name else new_name) |
219 | + verbose_copy(src_path, dst_path, console, current_file, total_files) |
220 | + current_file += 1 |
221 | + else: |
222 | + for root, dirs, files in os.walk(src_path): |
223 | + for file in files: |
224 | + src_file_path = os.path.join(root, file) |
225 | + relative_path = os.path.relpath(src_file_path, start=src_path) |
226 | + dst_file_path = os.path.join(dst_dir, os.path.basename(src_path) if not new_name else new_name, relative_path) |
227 | + os.makedirs(os.path.dirname(dst_file_path), exist_ok=True) |
228 | + verbose_copy(src_file_path, dst_file_path, console, current_file, total_files) |
229 | + current_file += 1 |
230 | + else: |
231 | + with Progress( |
232 | + BarColumn(bar_width=50), |
233 | + "[progress.percentage]{task.percentage:>3.0f}%", |
234 | + TimeRemainingColumn(), |
235 | + "[#ea2a6f][[/#ea2a6f]", |
236 | + FileSizeColumn(), |
237 | + "[#ea2a6f]/[/#ea2a6f]", |
238 | + TextColumn("[bold cyan]{task.fields[total_size]}[/bold cyan]"), |
239 | + "[#ea2a6f]][/#ea2a6f]", |
240 | + console=console, |
241 | + auto_refresh=False |
242 | + ) as progress: |
243 | + task = progress.add_task("", total=total_size, total_size=format_file_size(total_size)) |
244 | + |
245 | + for src_path in srcPaths: |
246 | + if os.path.isfile(src_path): |
247 | + dst_file_path = os.path.join(dst_dir, os.path.basename(src_path) if not new_name else new_name) |
248 | + regular_copy(src_path, dst_file_path, console, task, progress) |
249 | + else: |
250 | + for root, dirs, files in os.walk(src_path): |
251 | + for file in files: |
252 | + src_file_path = os.path.join(root, file) |
253 | + relative_path = os.path.relpath(src_file_path, start=src_path) |
254 | + dst_file_path = os.path.join(dst_dir, os.path.basename(src_path) if not new_name else new_name, relative_path) |
255 | + os.makedirs(os.path.dirname(dst_file_path), exist_ok=True) |
256 | + regular_copy(src_file_path, dst_file_path, console, task, progress) |
257 | + |
258 | + return srcPaths |
259 | + |
260 | + |
261 | +def copy(): |
262 | + run('Copying') |
263 | + |
264 | + |
265 | +def move(): |
266 | + src_paths = run('Moving') |
267 | + for src_path in src_paths: |
268 | + if os.path.isfile(src_path): |
269 | + os.remove(src_path) |
270 | + else: |
271 | + shutil.rmtree(src_path) |
272 | + |
273 | + |
274 | +def hlp(): |
275 | + print(""" |
276 | +usage: ptrack [-h] [-v] [-c] [-m] |
277 | + |
278 | +A simple CLI utility for asthetically tracking progress when copying or moving files. |
279 | + |
280 | +options: |
281 | + -h, --help show this help message and exit |
282 | + -v, --verbose verbose output |
283 | + -c, --copy copy files (You can use `ptc` instead of `ptrack -c`) |
284 | + -m, --move move files (You can use `ptm` instead of `ptrack -m`) |
285 | +""") |
286 | + |
287 | + |
288 | +def main(): |
289 | + if argMove: |
290 | + move() |
291 | + elif argCopy: |
292 | + copy() |
293 | + else: |
294 | + hlp() |
295 | + |
296 | +if __name__ == "__main__": |
297 | + main() |
298 | diff --git a/setup.py b/setup.py |
299 | new file mode 100644 |
300 | index 0000000..fdea560 |
301 | --- /dev/null |
302 | +++ b/setup.py |
303 | @@ -0,0 +1,22 @@ |
304 | +from setuptools import setup, find_packages |
305 | + |
306 | +setup( |
307 | + name='ptrack', |
308 | + version='0.1.0', |
309 | + description='A simple CLI utility for asthetically tracking progress when copying or moving files.', |
310 | + author='Connor Etherington', |
311 | + author_email='[email protected]', |
312 | + packages=find_packages(), |
313 | + install_requires=[ |
314 | + 'rich', |
315 | + 'argparse', |
316 | + 'argcomplete', |
317 | + ], |
318 | + entry_points={ |
319 | + 'console_scripts': [ |
320 | + 'ptc=ptrack.main:copy', |
321 | + 'ptm=ptrack.main:move', |
322 | + 'ptrack=ptrack.main:main', |
323 | + ] |
324 | + } |
325 | +) |