ptrack


Logs | Files | README | README | LICENSE | LICENSE | GitLab


1
commit 1b80b36aa234b1be35ba6513c0e2ece734791476
2
Author: Connor Etherington <[email protected]>
3
Date:   Tue Sep 19 23:12:10 2023 +0200
4
5
    Auto-Commit Update - 20230919
6
---
7
 PKGBUILD                             |   4 +-
8
 README.md                            |   2 +-
9
 build/lib/ptrack/__init__.py         |  16 +++
10
 build/lib/ptrack/main.py             | 215 +++++++++++++++++++++++++++++++++++
11
 build/lib/ptrack/methods.py          | 147 ++++++++++++++++++++++++
12
 dist/ptrack-1.0.0-py3-none-any.whl   | Bin 0 -> 6744 bytes
13
 dist/ptrack-1.0.0.tar.gz             | Bin 0 -> 6921 bytes
14
 ptrack.egg-info/PKG-INFO             |  13 +++
15
 ptrack.egg-info/SOURCES.txt          |  12 ++
16
 ptrack.egg-info/dependency_links.txt |   1 +
17
 ptrack.egg-info/entry_points.txt     |   5 +
18
 ptrack.egg-info/requires.txt         |   6 +
19
 ptrack.egg-info/top_level.txt        |   1 +
20
 ptrack/__init__.py                   |   2 +-
21
 ptrack/main.py                       |   5 +
22
 ptrack/methods.py                    |   9 +-
23
 recipe/meta.yaml                     |   2 +-
24
 setup.py                             |   2 +-
25
 18 files changed, 435 insertions(+), 7 deletions(-)
26
27
diff --git a/PKGBUILD b/PKGBUILD
28
index 49eabb5..41df404 100644
29
--- a/PKGBUILD
30
+++ b/PKGBUILD
31
@@ -1,7 +1,7 @@
32
 # Maintainer: Connor Etherington <[email protected]>
33
 # ---
34
 pkgname=ptrack
35
-pkgver=0.2.5
36
+pkgver=1.0.0
37
 pkgrel=1
38
 pkgdesc="A simple CLI utility for asthetically tracking progress when copying, moving or downloading files."
39
 arch=(x86_64)
40
@@ -32,7 +32,7 @@ package() {
41
   cd "$srcdir/${pkgname}-${pkgver}-${pkgrel}-${arch}" ||
42
   cd "$srcdir/${pkgname}"
43
 
44
-  python3 ./setup.py install --root="$pkgdir" --prefix=/usr --optimize=1
45
+  pip3 install --root="${pkgdir}" --no-deps --ignore-installed ${pkgname}
46
 
47
   install -Dm644 LICENSE "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
48
   install -Dm644 README.md "${pkgdir}/usr/share/doc/${pkgname}/README.md"
49
diff --git a/README.md b/README.md
50
index 7943082..dd55b8b 100644
51
--- a/README.md
52
+++ b/README.md
53
@@ -3,7 +3,7 @@
54
 ### Welcome to ptrack, a powerful and user-friendly CLI utility for tracking the progress of your file operations.
55
 ### Designed to be as concise, efficient and performance-optimized, ptrack works swiftly and accurately, while providing insight into the progress of the task at hand.
56
 
57
-*Version: 0.2.5*
58
+*Version: 1.0.0*
59
 
60
 ***
61
 
62
diff --git a/build/lib/ptrack/__init__.py b/build/lib/ptrack/__init__.py
63
new file mode 100644
64
index 0000000..bf11915
65
--- /dev/null
66
+++ b/build/lib/ptrack/__init__.py
67
@@ -0,0 +1,16 @@
68
+import argparse
69
+version="1.0.0"
70
+
71
+parser = argparse.ArgumentParser(description='A simple CLI utility for asthetically tracking progress when copying or moving files.')
72
+parser.add_argument('-v', '--verbose', action='store_true', help='verbose output')
73
+parser.add_argument('-c', '--copy', action='store_true', help='copy files (You can use `ptc` instead of `ptrack -c`)')
74
+parser.add_argument('-m', '--move', action='store_true', help='move files (You can use `ptm` instead of `ptrack -m`)')
75
+parser.add_argument('-d', '--download', action='store_true', help='download files (You can use `ptd` instead of `ptrack -d`)')
76
+parser.add_argument('-V', '--version', action='version', version='%(prog)s' + version)
77
+
78
+args, unknown = parser.parse_known_args()
79
+
80
+verbose = args.verbose
81
+copy = args.copy
82
+move = args.move
83
+download = args.download
84
diff --git a/build/lib/ptrack/main.py b/build/lib/ptrack/main.py
85
new file mode 100644
86
index 0000000..44e9aa6
87
--- /dev/null
88
+++ b/build/lib/ptrack/main.py
89
@@ -0,0 +1,215 @@
90
+import os
91
+import re
92
+import sys
93
+import ptrack
94
+from ptrack.methods import format_file_size, regular_copy, verbose_copy, hlp, getTotalSize, CustomFileSizeColumn
95
+from rich.progress import Progress, BarColumn, TextColumn, TimeRemainingColumn, FileSizeColumn
96
+from rich.console import Console
97
+from datetime import timedelta
98
+import shutil
99
+import requests
100
+import validators
101
+
102
+verbose = ptrack.verbose
103
+argCopy = ptrack.copy
104
+argMove = ptrack.move
105
+argDownload = ptrack.download
106
+
107
+
108
+def run(process):
109
+    console = Console()
110
+
111
+    if len(sys.argv) < 3:
112
+        hlp()
113
+        if process == "Copying":
114
+            console.print("[bold cyan]Usage: ptc [OPTIONS] SOURCE... DESTINATION[/bold cyan]")
115
+        elif process == "Moving":
116
+            console.print("[bold cyan]Usage: ptm [OPTIONS] SOURCE... DESTINATION[/bold cyan]")
117
+        sys.exit(1)
118
+
119
+    src_paths = sys.argv[1:-1]
120
+    dst = sys.argv[-1]
121
+    srcPaths = []
122
+
123
+    for path in src_paths:
124
+        if path.endswith('/'):
125
+            path = path[:-1]
126
+        srcPaths.append(path)
127
+
128
+    if os.path.isdir(dst):
129
+        dst_dir = dst
130
+        new_name = None
131
+    else:
132
+        dst_dir = os.path.dirname(dst)
133
+        new_name = os.path.basename(dst)
134
+
135
+    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))
136
+    total_size = getTotalSize(srcPaths)
137
+    destination_path = os.path.join(dst_dir, os.path.basename(srcPaths[0]) if not new_name else new_name)
138
+
139
+    current_file = 1
140
+
141
+    if total_files > 1:
142
+        console.print(f"\n[#ea2a6f]{process}:[/#ea2a6f] [bold cyan]{total_files} files[/bold cyan]\n")
143
+    else:
144
+        for src_path in srcPaths:
145
+            if os.path.isfile(src_path):
146
+                console.print(f"\n[#ea2a6f]{process}:[/#ea2a6f] [bold cyan] {os.path.basename(src_path)} [/bold cyan]\n")
147
+
148
+    if verbose:
149
+        for src_path in srcPaths:
150
+            if os.path.isfile(src_path):
151
+                dst_path = os.path.join(dst_dir, os.path.basename(src_path) if not new_name else new_name)
152
+                terminate = verbose_copy(src_path, dst_path, console, current_file, total_files, file_name=os.path.basename(src_path))
153
+                current_file += 1
154
+                if terminate == 'c':
155
+                    console.print("\n[bold red]\[-][/bold red][bold white] Operation cancelled by user.[/bold white]\n")
156
+                    sys.exit(1)
157
+            else:
158
+                for root, dirs, files in os.walk(src_path):
159
+                    for file in files:
160
+                        src_file_path = os.path.join(root, file)
161
+                        relative_path = os.path.relpath(src_file_path, start=src_path)
162
+                        dst_file_path = os.path.join(dst_dir, os.path.basename(src_path) if not new_name else new_name, relative_path)
163
+                        os.makedirs(os.path.dirname(src_file_path), exist_ok=True)
164
+                        terminate = verbose_copy(src_file_path, dst_file_path, console, current_file, total_files, file_name=file)
165
+                        current_file += 1
166
+                        if terminate == 'c':
167
+                            console.print("\n[bold red]\[-][/bold red][bold white] Operation cancelled by user.[/bold white]\n")
168
+                            sys.exit(1)
169
+    else:
170
+        with Progress(
171
+            BarColumn(bar_width=50),
172
+            "[progress.percentage]{task.percentage:>3.0f}%",
173
+            TimeRemainingColumn(),
174
+            "[#ea2a6f][[/#ea2a6f]",
175
+            FileSizeColumn(),
176
+            "[#ea2a6f]/[/#ea2a6f]",
177
+            TextColumn("[bold cyan]{task.fields[total_size]}[/bold cyan]"),
178
+            "[#ea2a6f]][/#ea2a6f]",
179
+            TextColumn("-[bold yellow] {task.fields[current_file_name]}[/bold yellow]"),
180
+            console=console,
181
+            auto_refresh=False
182
+        ) as progress:
183
+            task = progress.add_task("", total=total_size, total_size=format_file_size(total_size), current_file_name="Initializing...")
184
+
185
+            try:
186
+                for src_path in srcPaths:
187
+                    if os.path.isfile(src_path):
188
+                        src_file_path = src_path
189
+                        dst_file_path = os.path.join(dst_dir, os.path.basename(src_path) if not new_name else new_name)
190
+                        file_premissions = os.stat(src_file_path).st_mode
191
+                        progress.update(task, current_file_name=os.path.basename(src_path), refresh=True)  # Force refresh
192
+                        terminate = regular_copy(src_path, dst_file_path, console, task, progress, file_name=os.path.basename(src_path))
193
+                        if terminate == 'c':
194
+                            console.print("\n[bold red]\[-][/bold red][bold white] Operation cancelled by user.[/bold white]\n")
195
+                            sys.exit(1)
196
+                        else:
197
+                            os.chmod(dst_file_path, file_premissions)
198
+
199
+                    else:
200
+                        for root, dirs, files in os.walk(src_path):
201
+                            for file in files:
202
+                                src_file_path = os.path.join(root, file)
203
+                                relative_path = os.path.relpath(src_file_path, start=src_path)
204
+                                dst_file_path = os.path.join(dst_dir, os.path.basename(src_path) if not new_name else new_name, relative_path)
205
+                                os.makedirs(os.path.dirname(dst_file_path), exist_ok=True)
206
+                                progress.update(task, current_file_name=file, refresh=True)  # Force refresh
207
+                                regular_copy(src_file_path, dst_file_path, console, task, progress, file_name=file)
208
+
209
+            except KeyboardInterrupt:
210
+                console.print("\n[bold red]\[-][/bold red][bold white] Operation cancelled by user.[/bold white]\n")
211
+                sys.exit(1)
212
+
213
+    return srcPaths
214
+
215
+
216
+def download():
217
+    console = Console()
218
+    urls = sys.argv[1:]
219
+
220
+    if len(urls) == 0:
221
+        console.print("\n[bold red][-][/bold red] No URL provided.\n")
222
+        sys.exit()
223
+
224
+    num_urls = len(urls)
225
+    for url in urls:
226
+        if url.startswith('-'):
227
+            num_urls -= 1
228
+        elif not validators.url(url):
229
+            console.print(f"\n[bold red][-][/bold red] Invalid URL: [bold yellow]{url}[/bold yellow]\n")
230
+            sys.exit()
231
+
232
+    console.print(f"\n[#ea2a6f]Downloading:[/#ea2a6f] [bold yellow]{num_urls}[/bold yellow] [bold cyan]files[/bold cyan]\n")
233
+
234
+    errors = []
235
+    for url in urls:
236
+        try:
237
+            if url.startswith('-'):
238
+                continue
239
+
240
+            response = requests.get(url, stream=True, allow_redirects=True)
241
+            total_size_in_bytes = int(response.headers.get('content-length', 0))
242
+            content_disposition = response.headers.get('content-disposition')
243
+            destination_path = re.findall('filename="(.+)"', content_disposition)[0] if content_disposition and re.findall('filename="(.+)"', content_disposition) else os.path.basename(url)
244
+
245
+            with Progress(
246
+                BarColumn(bar_width=50),
247
+                "[progress.percentage]{task.percentage:>3.0f}%",
248
+                TimeRemainingColumn(),
249
+                "[#ea2a6f][[/#ea2a6f]",
250
+                CustomFileSizeColumn(),
251
+                "[#ea2a6f]][/#ea2a6f]",
252
+                f" {destination_path}",  # This line will print the filename at the end
253
+                console=console,
254
+                auto_refresh=True
255
+            ) as progress:
256
+                task_id = progress.add_task("Downloading", total=total_size_in_bytes)
257
+                block_size = 1024  # 1 Kibibyte
258
+                with open(destination_path, 'wb') as file:
259
+                    for data in response.iter_content(block_size):
260
+                        file.write(data)
261
+                        progress.update(task_id, advance=block_size)
262
+        except KeyboardInterrupt:
263
+            console.print("\n[bold red]\[-][/bold red][bold white] Operation cancelled by user.[/bold white]\n")
264
+            sys.exit(1)
265
+
266
+        except Exception as e:
267
+            console.print(f"\n[bold red]\[-][/bold red][bold white] Could not download file: [bold yellow]{url}[/bold yellow]\n")
268
+            print(e)
269
+            errors.append(url)
270
+
271
+    if len(errors) == 0:
272
+        console.print("\n[bold green]Download completed![/bold green]\n")
273
+    else:
274
+        console.print("[bold red]The following files could not be downloaded:[/bold red]\n")
275
+        for error in errors:
276
+            console.print(f"[bold red]   -[/bold red][bold yellow]{error}[/bold yellow]\n")
277
+
278
+
279
+def copy():
280
+    run('Copying')
281
+
282
+
283
+def move():
284
+    src_paths = run('Moving')
285
+    for src_path in src_paths:
286
+        if os.path.isfile(src_path):
287
+            os.remove(src_path)
288
+        else:
289
+            shutil.rmtree(src_path)
290
+
291
+
292
+def main():
293
+    if argMove:
294
+        move()
295
+    elif argCopy:
296
+        copy()
297
+    elif argDownload:
298
+        download()
299
+    else:
300
+        hlp()
301
+
302
+
303
+if __name__ == "__main__":
304
+    main()
305
diff --git a/build/lib/ptrack/methods.py b/build/lib/ptrack/methods.py
306
new file mode 100644
307
index 0000000..01a2d63
308
--- /dev/null
309
+++ b/build/lib/ptrack/methods.py
310
@@ -0,0 +1,147 @@
311
+import os
312
+import sys
313
+import requests
314
+from rich.console import Console
315
+from rich.progress import Progress, TextColumn, BarColumn, TimeRemainingColumn, FileSizeColumn, Task, DownloadColumn, TimeElapsedColumn
316
+from rich.text import Text
317
+from datetime import timedelta
318
+from humanize import naturalsize
319
+import shutil
320
+
321
+console = Console()
322
+operation_cancelled = False
323
+
324
+
325
+def getTotalSize(srcPaths):
326
+    total_size = 0
327
+    for path in srcPaths:
328
+        if os.path.isfile(path):
329
+            total_size += os.path.getsize(path)
330
+        else:
331
+            for r, d, files in os.walk(path):
332
+                for f in files:
333
+                    fp = os.path.join(r, f)
334
+                    total_size += os.path.getsize(fp)
335
+    return total_size
336
+
337
+
338
+def format_file_size(file_size):
339
+    if file_size >= 1000 ** 4:  # Terabyte
340
+        return f"{round(file_size / (1000 ** 4))} TB"
341
+    elif file_size >= 1000 ** 3:  # Gigabyte
342
+        return f"{round(file_size / (1000 ** 3))} GB"
343
+    elif file_size >= 1000 ** 2:  # Megabyte
344
+        return f"{round(file_size / (1000 ** 2))} MB"
345
+    elif file_size >= 1000:  # Kilobyte
346
+        return f"{round(file_size / 1000)} kB"
347
+    else:  # Byte
348
+        return f"{file_size} bytes"
349
+
350
+
351
+
352
+def regular_copy(src, dst, console, task, progress, file_name):
353
+
354
+    global operation_cancelled
355
+
356
+    try:
357
+        with open(src, 'rb') as fsrc, open(dst, 'wb') as fdst:
358
+            while True:
359
+                buf = fsrc.read(1024*1024)
360
+                if not buf or operation_cancelled:
361
+                    break
362
+                filePremissions = os.stat(src).st_mode
363
+                fdst.write(buf)
364
+                progress.update(task, advance=len(buf))
365
+                progress.refresh()
366
+
367
+                os.chmod(dst, filePremissions)
368
+
369
+    except KeyboardInterrupt:
370
+        operation_cancelled = True
371
+        progress.stop()
372
+        return "c"
373
+
374
+
375
+def verbose_copy(src, dst, console, current, total_files, file_name):
376
+    operation_cancelled = False
377
+    file_size = os.path.getsize(src)
378
+
379
+    with Progress(
380
+        BarColumn(bar_width=50),
381
+        "[progress.percentage]{task.percentage:>3.0f}%",
382
+        TimeRemainingColumn(),
383
+        "[#ea2a6f][[/#ea2a6f]",
384
+        FileSizeColumn(),
385
+        "[#ea2a6f]/[/#ea2a6f]",
386
+        TextColumn(f"[bold cyan]{format_file_size(file_size)}[/bold cyan]"),
387
+        "[#ea2a6f]][/#ea2a6f]",
388
+        f"({current} of {total_files}) - {file_name}",
389
+        console=console,
390
+        auto_refresh=False
391
+    ) as progress:
392
+        task = progress.add_task("", total=file_size, file_size=format_file_size(file_size))
393
+
394
+        try:
395
+            with open(src, 'rb') as fsrc, open(dst, 'wb') as fdst:
396
+                while not progress.finished:
397
+                    buf = fsrc.read(1024*1024)
398
+                    if not buf or operation_cancelled:
399
+                        break
400
+                    fdst.write(buf)
401
+                    progress.update(task, advance=len(buf))
402
+                    progress.refresh()
403
+
404
+                    shutil.copystat(src, dst)
405
+
406
+        except KeyboardInterrupt:
407
+            operation_cancelled = True
408
+            progress.stop()
409
+            return "c"
410
+
411
+
412
+def hlp():
413
+    print("""
414
+usage: ptrack [-h] [-v] [-c] [-m] [-d] [-V]
415
+
416
+A simple CLI utility for asthetically tracking progress when copying or moving files.
417
+
418
+options:
419
+  -h, --help      show this help message and exit
420
+  -v, --verbose   verbose output
421
+  -c, --copy      copy files (You can use `ptc` instead of `ptrack -c`)
422
+  -m, --move      move files (You can use `ptm` instead of `ptrack -m`)
423
+  -d, --download  download files (You can use `ptd` instead of `ptrack -d`)
424
+  -V, --version   show program's version number and exit
425
+""")
426
+
427
+
428
+class CustomFileSizeColumn(FileSizeColumn, TimeElapsedColumn):
429
+    def render(self, task):
430
+        completed = task.completed
431
+        total = task.total
432
+        elapsed = task.elapsed
433
+
434
+        if elapsed > 0.0:  # Prevent division by zero
435
+            download_speed = completed / elapsed  # calculate download rate
436
+        else:
437
+            download_speed = 0
438
+
439
+        if total:
440
+            size = Text.assemble(
441
+                (f"{self._human_readable_size(completed)}", "green"),  # completed
442
+                (" / ", "none"),  # separator
443
+                (f"{self._human_readable_size(total)}", "red"),  # total
444
+                (" [", "none"),  # opening square bracket
445
+                (f"{self._human_readable_size(download_speed)}/s", "blue"),  # download rate
446
+                ("]", "none"),  # closing square bracket
447
+            )
448
+        else:
449
+            size = Text(str(self._human_readable_size(completed)))
450
+        return size
451
+
452
+    def _human_readable_size(self, size: int) -> str:
453
+        for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
454
+            if abs(size) < 1024.0:
455
+                return f"{size:.1f}{unit}"
456
+            size /= 1024.0
457
+        return f"{size:.1f}PB"
458
diff --git a/dist/ptrack-1.0.0-py3-none-any.whl b/dist/ptrack-1.0.0-py3-none-any.whl
459
new file mode 100644
460
index 0000000..c1fa573
461
Binary files /dev/null and b/dist/ptrack-1.0.0-py3-none-any.whl differ
462
diff --git a/dist/ptrack-1.0.0.tar.gz b/dist/ptrack-1.0.0.tar.gz
463
new file mode 100644
464
index 0000000..c127219
465
Binary files /dev/null and b/dist/ptrack-1.0.0.tar.gz differ
466
diff --git a/ptrack.egg-info/PKG-INFO b/ptrack.egg-info/PKG-INFO
467
new file mode 100644
468
index 0000000..e28cefc
469
--- /dev/null
470
+++ b/ptrack.egg-info/PKG-INFO
471
@@ -0,0 +1,13 @@
472
+Metadata-Version: 2.1
473
+Name: ptrack
474
+Version: 1.0.0
475
+Summary: A simple CLI utility for asthetically tracking progress when copying, moving or downloading files.
476
+Author: Connor Etherington
477
+Author-email: [email protected]
478
+License-File: LICENSE
479
+Requires-Dist: rich
480
+Requires-Dist: argparse
481
+Requires-Dist: requests
482
+Requires-Dist: validators
483
+Requires-Dist: setuptools
484
+Requires-Dist: humanize
485
diff --git a/ptrack.egg-info/SOURCES.txt b/ptrack.egg-info/SOURCES.txt
486
new file mode 100644
487
index 0000000..086a784
488
--- /dev/null
489
+++ b/ptrack.egg-info/SOURCES.txt
490
@@ -0,0 +1,12 @@
491
+LICENSE
492
+README.md
493
+setup.py
494
+ptrack/__init__.py
495
+ptrack/main.py
496
+ptrack/methods.py
497
+ptrack.egg-info/PKG-INFO
498
+ptrack.egg-info/SOURCES.txt
499
+ptrack.egg-info/dependency_links.txt
500
+ptrack.egg-info/entry_points.txt
501
+ptrack.egg-info/requires.txt
502
+ptrack.egg-info/top_level.txt
503
diff --git a/ptrack.egg-info/dependency_links.txt b/ptrack.egg-info/dependency_links.txt
504
new file mode 100644
505
index 0000000..8b13789
506
--- /dev/null
507
+++ b/ptrack.egg-info/dependency_links.txt
508
@@ -0,0 +1 @@
509
+
510
diff --git a/ptrack.egg-info/entry_points.txt b/ptrack.egg-info/entry_points.txt
511
new file mode 100644
512
index 0000000..ea851b3
513
--- /dev/null
514
+++ b/ptrack.egg-info/entry_points.txt
515
@@ -0,0 +1,5 @@
516
+[console_scripts]
517
+ptc = ptrack.main:copy
518
+ptd = ptrack.main:download
519
+ptm = ptrack.main:move
520
+ptrack = ptrack.main:main
521
diff --git a/ptrack.egg-info/requires.txt b/ptrack.egg-info/requires.txt
522
new file mode 100644
523
index 0000000..d19a378
524
--- /dev/null
525
+++ b/ptrack.egg-info/requires.txt
526
@@ -0,0 +1,6 @@
527
+rich
528
+argparse
529
+requests
530
+validators
531
+setuptools
532
+humanize
533
diff --git a/ptrack.egg-info/top_level.txt b/ptrack.egg-info/top_level.txt
534
new file mode 100644
535
index 0000000..c003217
536
--- /dev/null
537
+++ b/ptrack.egg-info/top_level.txt
538
@@ -0,0 +1 @@
539
+ptrack
540
diff --git a/ptrack/__init__.py b/ptrack/__init__.py
541
index a329509..bf11915 100644
542
--- a/ptrack/__init__.py
543
+++ b/ptrack/__init__.py
544
@@ -1,5 +1,5 @@
545
 import argparse
546
-version="0.2.5"
547
+version="1.0.0"
548
 
549
 parser = argparse.ArgumentParser(description='A simple CLI utility for asthetically tracking progress when copying or moving files.')
550
 parser.add_argument('-v', '--verbose', action='store_true', help='verbose output')
551
diff --git a/ptrack/main.py b/ptrack/main.py
552
index a0b26dc..44e9aa6 100644
553
--- a/ptrack/main.py
554
+++ b/ptrack/main.py
555
@@ -96,12 +96,17 @@ def run(process):
556
             try:
557
                 for src_path in srcPaths:
558
                     if os.path.isfile(src_path):
559
+                        src_file_path = src_path
560
                         dst_file_path = os.path.join(dst_dir, os.path.basename(src_path) if not new_name else new_name)
561
+                        file_premissions = os.stat(src_file_path).st_mode
562
                         progress.update(task, current_file_name=os.path.basename(src_path), refresh=True)  # Force refresh
563
                         terminate = regular_copy(src_path, dst_file_path, console, task, progress, file_name=os.path.basename(src_path))
564
                         if terminate == 'c':
565
                             console.print("\n[bold red]\[-][/bold red][bold white] Operation cancelled by user.[/bold white]\n")
566
                             sys.exit(1)
567
+                        else:
568
+                            os.chmod(dst_file_path, file_premissions)
569
+
570
                     else:
571
                         for root, dirs, files in os.walk(src_path):
572
                             for file in files:
573
diff --git a/ptrack/methods.py b/ptrack/methods.py
574
index 2e9677e..01a2d63 100644
575
--- a/ptrack/methods.py
576
+++ b/ptrack/methods.py
577
@@ -6,6 +6,7 @@ from rich.progress import Progress, TextColumn, BarColumn, TimeRemainingColumn,
578
 from rich.text import Text
579
 from datetime import timedelta
580
 from humanize import naturalsize
581
+import shutil
582
 
583
 console = Console()
584
 operation_cancelled = False
585
@@ -45,13 +46,16 @@ def regular_copy(src, dst, console, task, progress, file_name):
586
     try:
587
         with open(src, 'rb') as fsrc, open(dst, 'wb') as fdst:
588
             while True:
589
-                buf = fsrc.read(1024 * 128)  # Reduced buffer size for more frequent checks
590
+                buf = fsrc.read(1024*1024)
591
                 if not buf or operation_cancelled:
592
                     break
593
+                filePremissions = os.stat(src).st_mode
594
                 fdst.write(buf)
595
                 progress.update(task, advance=len(buf))
596
                 progress.refresh()
597
 
598
+                os.chmod(dst, filePremissions)
599
+
600
     except KeyboardInterrupt:
601
         operation_cancelled = True
602
         progress.stop()
603
@@ -86,6 +90,9 @@ def verbose_copy(src, dst, console, current, total_files, file_name):
604
                     fdst.write(buf)
605
                     progress.update(task, advance=len(buf))
606
                     progress.refresh()
607
+
608
+                    shutil.copystat(src, dst)
609
+
610
         except KeyboardInterrupt:
611
             operation_cancelled = True
612
             progress.stop()
613
diff --git a/recipe/meta.yaml b/recipe/meta.yaml
614
index 62dc45a..45860df 100644
615
--- a/recipe/meta.yaml
616
+++ b/recipe/meta.yaml
617
@@ -1,6 +1,6 @@
618
 package:
619
   name: ptrack
620
-  version: 0.2.5
621
+  version: 1.0.0
622
 
623
 source:
624
   path: ..
625
diff --git a/setup.py b/setup.py
626
index 2c8aa5c..1bfa98a 100644
627
--- a/setup.py
628
+++ b/setup.py
629
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
630
 
631
 setup(
632
     name='ptrack',
633
-    version="0.2.5",
634
+    version="1.0.0",
635
     description='A simple CLI utility for asthetically tracking progress when copying, moving or downloading files.',
636
     author='Connor Etherington',
637
     author_email='[email protected]',