ccfx 1.0.8__tar.gz → 1.0.9__tar.gz
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.
- {ccfx-1.0.8/ccfx.egg-info → ccfx-1.0.9}/PKG-INFO +1 -1
- {ccfx-1.0.8 → ccfx-1.0.9}/ccfx/ccfx.py +69 -16
- {ccfx-1.0.8 → ccfx-1.0.9/ccfx.egg-info}/PKG-INFO +1 -1
- {ccfx-1.0.8 → ccfx-1.0.9}/pyproject.toml +1 -1
- {ccfx-1.0.8 → ccfx-1.0.9}/LICENSE +0 -0
- {ccfx-1.0.8 → ccfx-1.0.9}/MANIFEST.in +0 -0
- {ccfx-1.0.8 → ccfx-1.0.9}/README.md +0 -0
- {ccfx-1.0.8 → ccfx-1.0.9}/ccfx/__init__.py +0 -0
- {ccfx-1.0.8 → ccfx-1.0.9}/ccfx/excel.py +0 -0
- {ccfx-1.0.8 → ccfx-1.0.9}/ccfx/mssqlConnection.py +0 -0
- {ccfx-1.0.8 → ccfx-1.0.9}/ccfx/sqliteConnection.py +0 -0
- {ccfx-1.0.8 → ccfx-1.0.9}/ccfx/word.py +0 -0
- {ccfx-1.0.8 → ccfx-1.0.9}/ccfx.egg-info/SOURCES.txt +0 -0
- {ccfx-1.0.8 → ccfx-1.0.9}/ccfx.egg-info/dependency_links.txt +0 -0
- {ccfx-1.0.8 → ccfx-1.0.9}/ccfx.egg-info/requires.txt +0 -0
- {ccfx-1.0.8 → ccfx-1.0.9}/ccfx.egg-info/top_level.txt +0 -0
- {ccfx-1.0.8 → ccfx-1.0.9}/setup.cfg +0 -0
@@ -479,9 +479,9 @@ def alert(message:str, server:str = "http://ntfy.sh", topic:str = "pythonAlerts"
|
|
479
479
|
return: True if the alert was sent successfully, False otherwise
|
480
480
|
'''
|
481
481
|
print(message) if printIt else None; header_data = {}
|
482
|
-
if not messageTitle is None: header_data["Title"] = messageTitle
|
483
|
-
if not priority is None: header_data["Priority"] = priority
|
484
|
-
if not len(tags) == 0: header_data["Tags"] = ",".join(tags)
|
482
|
+
if not messageTitle is None: header_data["Title"] = str(messageTitle).replace("\r"," ").replace("\n"," ")
|
483
|
+
if not priority is None: header_data["Priority"] = str(int(priority))
|
484
|
+
if not len(tags) == 0: header_data["Tags"] = ",".join(map(str, tags))
|
485
485
|
|
486
486
|
try:
|
487
487
|
if v: print(f"sending alert to {server}/{topic}")
|
@@ -517,6 +517,7 @@ def deletePath(path:str, v:bool = False) -> bool:
|
|
517
517
|
if v:
|
518
518
|
print(f'! {path} does not exist')
|
519
519
|
deleted = False
|
520
|
+
return deleted
|
520
521
|
|
521
522
|
|
522
523
|
def downloadChunk(url: str, start: int, end: int, path: str) -> None:
|
@@ -528,6 +529,46 @@ def downloadChunk(url: str, start: int, end: int, path: str) -> None:
|
|
528
529
|
f.write(chunk)
|
529
530
|
|
530
531
|
|
532
|
+
def correctFisheye(inputFile: str, outputFile: str = '',
|
533
|
+
k1: float = -0.1, k2: float = 0.05,
|
534
|
+
cx: float = 0.5, cy: float = 0.5,
|
535
|
+
crf: int = 20) -> str:
|
536
|
+
"""
|
537
|
+
Correct fisheye distortion in a video and save as MP4.
|
538
|
+
|
539
|
+
Args:
|
540
|
+
inputFile (str): Path to the input video (any format).
|
541
|
+
outputFile (str, optional): Path for the corrected MP4. If None, auto-creates.
|
542
|
+
k1, k2 (float): Lens distortion coefficients.
|
543
|
+
cx, cy (float): Optical center (0.5 = image center).
|
544
|
+
crf (int): Constant Rate Factor (lower = better quality, larger file).
|
545
|
+
|
546
|
+
Returns:
|
547
|
+
str: Path to the corrected MP4 file.
|
548
|
+
"""
|
549
|
+
if not os.path.exists(inputFile):
|
550
|
+
raise FileNotFoundError(f"Input file not found: {inputFile}")
|
551
|
+
|
552
|
+
# Default output path
|
553
|
+
if outputFile == '':
|
554
|
+
base, _ = os.path.splitext(inputFile)
|
555
|
+
outputFile = f"{base}_corrected.mp4"
|
556
|
+
|
557
|
+
cmd = [
|
558
|
+
"ffmpeg", "-hide_banner", "-loglevel", "error", "-stats",
|
559
|
+
"-i", inputFile,
|
560
|
+
"-vf", f"lenscorrection=cx={cx}:cy={cy}:k1={k1}:k2={k2}",
|
561
|
+
"-c:v", "libx264", "-preset", "slow", f"-crf", str(crf),
|
562
|
+
"-pix_fmt", "yuv420p",
|
563
|
+
"-c:a", "aac", "-b:a", "192k",
|
564
|
+
"-movflags", "+faststart",
|
565
|
+
outputFile
|
566
|
+
]
|
567
|
+
|
568
|
+
subprocess.run(cmd, check=True)
|
569
|
+
return outputFile
|
570
|
+
|
571
|
+
|
531
572
|
def formatStringBlock(input_str: str, max_chars: int = 70) -> str:
|
532
573
|
'''
|
533
574
|
This function takes a string and formats it into a block of text
|
@@ -558,7 +599,6 @@ def formatStringBlock(input_str: str, max_chars: int = 70) -> str:
|
|
558
599
|
|
559
600
|
|
560
601
|
|
561
|
-
|
562
602
|
def downloadFile(url: str, save_path: str, exists_action: str = 'resume', num_connections: int = 5, v: bool = False) -> None:
|
563
603
|
if v:
|
564
604
|
print(f"\ndownloading {url}")
|
@@ -573,13 +613,17 @@ def downloadFile(url: str, save_path: str, exists_action: str = 'resume', num_co
|
|
573
613
|
if os.path.exists(save_fname):
|
574
614
|
if exists_action == 'skip':
|
575
615
|
if v:
|
576
|
-
print(f"
|
616
|
+
print(f"file exists, skipping: {save_fname}")
|
577
617
|
return
|
578
618
|
elif exists_action == 'overwrite':
|
579
619
|
os.remove(save_fname)
|
580
620
|
# 'resume' is handled below
|
581
621
|
|
582
|
-
# Get file size
|
622
|
+
# Get file size (suppress urllib3 warnings when v=False)
|
623
|
+
import urllib3
|
624
|
+
if not v:
|
625
|
+
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
626
|
+
|
583
627
|
response = requests.head(url)
|
584
628
|
file_size = int(response.headers.get('content-length', 0))
|
585
629
|
|
@@ -589,7 +633,7 @@ def downloadFile(url: str, save_path: str, exists_action: str = 'resume', num_co
|
|
589
633
|
initial_pos = os.path.getsize(save_fname)
|
590
634
|
if initial_pos >= file_size:
|
591
635
|
if v:
|
592
|
-
print(f"
|
636
|
+
print(f"file already completed: {save_fname}")
|
593
637
|
return
|
594
638
|
|
595
639
|
# Calculate chunk sizes
|
@@ -609,14 +653,22 @@ def downloadFile(url: str, save_path: str, exists_action: str = 'resume', num_co
|
|
609
653
|
executor.submit(downloadChunk, url, start, end, temp_files[i])
|
610
654
|
)
|
611
655
|
|
612
|
-
# Wait for all downloads to complete with progress bar
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
656
|
+
# Wait for all downloads to complete with progress bar (conditionally show progress)
|
657
|
+
if v:
|
658
|
+
with tqdm(total=file_size-initial_pos, initial=initial_pos, unit='B',
|
659
|
+
unit_scale=True, desc=fname) as pbar:
|
660
|
+
completed = initial_pos
|
661
|
+
while completed < file_size:
|
662
|
+
current = sum(os.path.getsize(f) for f in temp_files if os.path.exists(f))
|
663
|
+
pbar.update(current - completed)
|
664
|
+
completed = current
|
665
|
+
else:
|
666
|
+
# Wait silently without progress bar
|
667
|
+
while True:
|
617
668
|
current = sum(os.path.getsize(f) for f in temp_files if os.path.exists(f))
|
618
|
-
|
619
|
-
|
669
|
+
if current >= file_size - initial_pos:
|
670
|
+
break
|
671
|
+
time.sleep(0.1)
|
620
672
|
|
621
673
|
# Merge chunks
|
622
674
|
with open(save_fname, 'ab' if initial_pos > 0 else 'wb') as outfile:
|
@@ -627,6 +679,7 @@ def downloadFile(url: str, save_path: str, exists_action: str = 'resume', num_co
|
|
627
679
|
os.remove(temp_file)
|
628
680
|
|
629
681
|
|
682
|
+
|
630
683
|
def mergeRasterTiles(tileList:list, outFile:str) -> str:
|
631
684
|
'''
|
632
685
|
Merge raster tiles into one raster file
|
@@ -1588,9 +1641,9 @@ def showProgress(count: int, end: int, message: str, barLength: int = 100) -> No
|
|
1588
1641
|
percentStr = f'{percent:03.1f}'
|
1589
1642
|
filled = int(barLength * count / end)
|
1590
1643
|
bar = '█' * filled + '░' * (barLength - filled)
|
1591
|
-
print(f'\r{message} |{bar}| {
|
1644
|
+
print(f'\r{message} |{bar}| {percentStr}% [{count}/{end}]', end='', flush=True)
|
1592
1645
|
if count == end:
|
1593
|
-
print(f'\r{message} |{bar}| {
|
1646
|
+
print(f'\r{message} |{bar}| {percentStr}% [{count}/{end}] ', end='', flush=True)
|
1594
1647
|
print()
|
1595
1648
|
|
1596
1649
|
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|