Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -21,16 +21,9 @@ from multiprocessing import Pool, cpu_count
|
|
| 21 |
from functools import partial
|
| 22 |
import tempfile
|
| 23 |
import shutil
|
|
|
|
| 24 |
|
| 25 |
# ========================== # Configuration and Setup # ==========================
|
| 26 |
-
# Use a temporary directory for storage to avoid file system issues on Hugging Face Spaces
|
| 27 |
-
TEMP_DIR = tempfile.mkdtemp(prefix="Ultralytics_")
|
| 28 |
-
os.environ['YOLO_CONFIG_DIR'] = TEMP_DIR
|
| 29 |
-
|
| 30 |
-
# Ensure output directory exists within temp directory
|
| 31 |
-
OUTPUT_DIR = os.path.join(TEMP_DIR, "output")
|
| 32 |
-
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
| 33 |
-
|
| 34 |
# Configure logging for better debugging
|
| 35 |
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
|
| 36 |
logger = logging.getLogger(__name__)
|
|
@@ -193,7 +186,6 @@ class BYTETracker:
|
|
| 193 |
CONFIG = {
|
| 194 |
"MODEL_PATH": "yolov8_safety.pt",
|
| 195 |
"FALLBACK_MODEL": "yolov8n.pt",
|
| 196 |
-
"OUTPUT_DIR": OUTPUT_DIR,
|
| 197 |
"VIOLATION_LABELS": {
|
| 198 |
0: "no_helmet",
|
| 199 |
1: "no_harness",
|
|
@@ -329,11 +321,11 @@ def calculate_safety_score(violations):
|
|
| 329 |
score = max(0, 100 - total_penalty)
|
| 330 |
return score
|
| 331 |
|
| 332 |
-
def generate_violation_pdf(violations, score):
|
| 333 |
"""Generate a PDF report for the detected violations"""
|
| 334 |
try:
|
| 335 |
pdf_filename = f"violations_{int(time.time())}.pdf"
|
| 336 |
-
pdf_path = os.path.join(
|
| 337 |
pdf_file = BytesIO()
|
| 338 |
c = canvas.Canvas(pdf_file, pagesize=letter)
|
| 339 |
|
|
@@ -503,23 +495,50 @@ def push_report_to_salesforce(violations, score, pdf_path, pdf_file):
|
|
| 503 |
logger.error(f"Salesforce record creation failed: {e}")
|
| 504 |
return "N/A", "Salesforce integration failed."
|
| 505 |
|
| 506 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 507 |
"""Process video to detect safety violations"""
|
| 508 |
-
video_path = None
|
|
|
|
|
|
|
|
|
|
|
|
|
| 509 |
try:
|
| 510 |
# Validate video data
|
| 511 |
if not video_data:
|
| 512 |
raise ValueError("Empty video data provided.")
|
| 513 |
|
| 514 |
-
# Log the size of the video data
|
| 515 |
logger.info(f"Received video data size: {len(video_data)} bytes")
|
| 516 |
if len(video_data) == 0:
|
| 517 |
raise ValueError("Video data is empty.")
|
| 518 |
|
| 519 |
-
# Save video to a temporary file
|
| 520 |
-
with tempfile.NamedTemporaryFile(suffix=".mp4", dir=
|
| 521 |
temp_file.write(video_data)
|
| 522 |
-
temp_file.flush()
|
| 523 |
video_path = temp_file.name
|
| 524 |
logger.info(f"Video saved to temporary file: {video_path}")
|
| 525 |
|
|
@@ -531,18 +550,9 @@ def process_video(video_data):
|
|
| 531 |
raise ValueError(f"Temporary video file is empty: {video_path}")
|
| 532 |
logger.info(f"Temporary video file size: {file_size} bytes")
|
| 533 |
|
| 534 |
-
#
|
| 535 |
-
|
| 536 |
-
|
| 537 |
-
f.read(1) # Test read access
|
| 538 |
-
logger.info(f"Temporary video file is readable: {video_path}")
|
| 539 |
-
except Exception as e:
|
| 540 |
-
raise IOError(f"Cannot read temporary video file {video_path}: {str(e)}")
|
| 541 |
-
|
| 542 |
-
# Open video with OpenCV
|
| 543 |
-
cap = cv2.VideoCapture(video_path)
|
| 544 |
-
if not cap.isOpened():
|
| 545 |
-
raise ValueError("Could not open video file. Ensure the video format is supported (e.g., MP4) and FFmpeg is installed.")
|
| 546 |
|
| 547 |
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
|
| 548 |
fps = cap.get(cv2.CAP_PROP_FPS) or 30
|
|
@@ -551,7 +561,6 @@ def process_video(video_data):
|
|
| 551 |
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
|
| 552 |
logger.info(f"Video properties: {duration:.2f}s, {total_frames} frames, {fps:.1f} FPS, {width}x{height}")
|
| 553 |
|
| 554 |
-
# Check if video is empty
|
| 555 |
if total_frames <= 0:
|
| 556 |
raise ValueError("Video has no frames.")
|
| 557 |
|
|
@@ -596,7 +605,6 @@ def process_video(video_data):
|
|
| 596 |
logger.info("No more frames to process.")
|
| 597 |
break
|
| 598 |
|
| 599 |
-
# Process batch with YOLO model
|
| 600 |
try:
|
| 601 |
results = model(batch_frames, device=device, conf=0.1, verbose=False)
|
| 602 |
except Exception as e:
|
|
@@ -659,7 +667,7 @@ def process_video(video_data):
|
|
| 659 |
detection = {
|
| 660 |
"worker_id": worker_id,
|
| 661 |
"violation": label,
|
| 662 |
-
"confidence": round(float(conf), 2),
|
| 663 |
"bounding_box": bbox,
|
| 664 |
"timestamp": current_time
|
| 665 |
}
|
|
@@ -678,7 +686,7 @@ def process_video(video_data):
|
|
| 678 |
)
|
| 679 |
|
| 680 |
snapshot_filename = f"violation_{label}_worker{worker_id}_{int(current_time*100)}.jpg"
|
| 681 |
-
snapshot_path = os.path.join(
|
| 682 |
|
| 683 |
cv2.imwrite(
|
| 684 |
snapshot_path,
|
|
@@ -692,24 +700,20 @@ def process_video(video_data):
|
|
| 692 |
"timestamp": current_time,
|
| 693 |
"snapshot_path": snapshot_path,
|
| 694 |
"snapshot_url": f"{CONFIG['PUBLIC_URL_BASE']}{snapshot_filename}",
|
| 695 |
-
"confidence": round(float(conf), 2)
|
| 696 |
})
|
| 697 |
|
| 698 |
logger.info(f"Captured snapshot for {label} violation by worker {worker_id} at {current_time:.2f}s")
|
| 699 |
|
| 700 |
-
# Ensure resources are released
|
| 701 |
cap.release()
|
| 702 |
-
|
| 703 |
processing_time = time.time() - start_time
|
| 704 |
logger.info(f"Processing complete in {processing_time:.2f}s")
|
| 705 |
|
| 706 |
-
# Log the snapshots for debugging
|
| 707 |
logger.info(f"Snapshots: {snapshots}")
|
| 708 |
|
| 709 |
violations = []
|
| 710 |
for worker_id, worker_violations in unique_violations.items():
|
| 711 |
for label, detection_time in worker_violations.items():
|
| 712 |
-
# Find the confidence from snapshots, ensuring it's a float
|
| 713 |
confidence = next(
|
| 714 |
(float(s["confidence"]) for s in snapshots if s["worker_id"] == worker_id and s["violation"] == label),
|
| 715 |
0.0
|
|
@@ -722,7 +726,6 @@ def process_video(video_data):
|
|
| 722 |
}
|
| 723 |
violations.append(violation)
|
| 724 |
|
| 725 |
-
# Log the violations for debugging
|
| 726 |
logger.info(f"Violations: {violations}")
|
| 727 |
|
| 728 |
if not violations:
|
|
@@ -731,12 +734,10 @@ def process_video(video_data):
|
|
| 731 |
return
|
| 732 |
|
| 733 |
score = calculate_safety_score(violations)
|
| 734 |
-
pdf_path, pdf_url, pdf_file = generate_violation_pdf(violations, score)
|
| 735 |
|
| 736 |
-
# Push to Salesforce with fallback
|
| 737 |
record_id, final_pdf_url = push_report_to_salesforce(violations, score, pdf_path, pdf_file)
|
| 738 |
|
| 739 |
-
# Generate violation table with robust error handling
|
| 740 |
violation_table = "| Violation | Worker ID | Time (s) | Confidence |\n"
|
| 741 |
violation_table += "|-----------|-----------|----------|------------|\n"
|
| 742 |
|
|
@@ -744,7 +745,6 @@ def process_video(video_data):
|
|
| 744 |
display_name = CONFIG["DISPLAY_NAMES"].get(v.get("violation", "Unknown"), "Unknown")
|
| 745 |
worker_id = v.get("worker_id", "Unknown")
|
| 746 |
timestamp = v.get("timestamp", 0.0)
|
| 747 |
-
# Ensure confidence is a valid float
|
| 748 |
try:
|
| 749 |
confidence = float(v.get("confidence", 0.0))
|
| 750 |
except (ValueError, TypeError) as e:
|
|
@@ -777,44 +777,62 @@ def process_video(video_data):
|
|
| 777 |
logger.error(f"Error processing video: {str(e)}", exc_info=True)
|
| 778 |
yield f"Error processing video: {str(e)}", "", "", "", ""
|
| 779 |
finally:
|
| 780 |
-
# Clean up the temporary video file
|
| 781 |
if video_path and os.path.exists(video_path):
|
| 782 |
try:
|
| 783 |
os.remove(video_path)
|
| 784 |
logger.info(f"Cleaned up temporary video file: {video_path}")
|
| 785 |
except Exception as e:
|
| 786 |
logger.error(f"Failed to clean up temporary video file {video_path}: {e}")
|
| 787 |
-
# Clean up temporary directory
|
| 788 |
-
if os.path.exists(TEMP_DIR):
|
| 789 |
-
shutil.rmtree(TEMP_DIR, ignore_errors=True)
|
| 790 |
-
logger.info(f"Cleaned up temporary directory: {TEMP_DIR}")
|
| 791 |
|
| 792 |
def gradio_interface(video_file):
|
| 793 |
"""Gradio interface for the video processing"""
|
| 794 |
-
|
| 795 |
-
|
| 796 |
-
|
| 797 |
try:
|
| 798 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 799 |
with open(video_file, "rb") as f:
|
| 800 |
video_data = f.read()
|
| 801 |
-
|
| 802 |
-
|
| 803 |
-
logger.info(f"Uploaded video file size: {len(video_data)} bytes")
|
| 804 |
if len(video_data) == 0:
|
| 805 |
return "Uploaded video file is empty.", "", "", "", ""
|
| 806 |
|
| 807 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 808 |
if not FFMPEG_AVAILABLE:
|
| 809 |
return "FFmpeg is not available in the environment. Please install FFmpeg to process videos.", "", "", "", ""
|
| 810 |
|
| 811 |
-
|
| 812 |
-
for status, score, snapshots_text, record_id, details_url in process_video(video_data):
|
| 813 |
yield status, score, snapshots_text, record_id, details_url
|
| 814 |
|
| 815 |
except Exception as e:
|
| 816 |
logger.error(f"Error in Gradio interface: {e}", exc_info=True)
|
| 817 |
yield f"Error: {str(e)}", "", "Error in processing.", "", ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 818 |
|
| 819 |
# ========================== # Gradio Interface # ==========================
|
| 820 |
interface = gr.Interface(
|
|
|
|
| 21 |
from functools import partial
|
| 22 |
import tempfile
|
| 23 |
import shutil
|
| 24 |
+
import tenacity
|
| 25 |
|
| 26 |
# ========================== # Configuration and Setup # ==========================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
# Configure logging for better debugging
|
| 28 |
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
|
| 29 |
logger = logging.getLogger(__name__)
|
|
|
|
| 186 |
CONFIG = {
|
| 187 |
"MODEL_PATH": "yolov8_safety.pt",
|
| 188 |
"FALLBACK_MODEL": "yolov8n.pt",
|
|
|
|
| 189 |
"VIOLATION_LABELS": {
|
| 190 |
0: "no_helmet",
|
| 191 |
1: "no_harness",
|
|
|
|
| 321 |
score = max(0, 100 - total_penalty)
|
| 322 |
return score
|
| 323 |
|
| 324 |
+
def generate_violation_pdf(violations, score, output_dir):
|
| 325 |
"""Generate a PDF report for the detected violations"""
|
| 326 |
try:
|
| 327 |
pdf_filename = f"violations_{int(time.time())}.pdf"
|
| 328 |
+
pdf_path = os.path.join(output_dir, pdf_filename)
|
| 329 |
pdf_file = BytesIO()
|
| 330 |
c = canvas.Canvas(pdf_file, pagesize=letter)
|
| 331 |
|
|
|
|
| 495 |
logger.error(f"Salesforce record creation failed: {e}")
|
| 496 |
return "N/A", "Salesforce integration failed."
|
| 497 |
|
| 498 |
+
@tenacity.retry(
|
| 499 |
+
stop=tenacity.stop_after_attempt(3),
|
| 500 |
+
wait=tenacity.wait_fixed(1),
|
| 501 |
+
retry=tenacity.retry_if_exception_type((IOError, OSError)),
|
| 502 |
+
before_sleep=lambda retry_state: logger.info(f"Retrying file access (attempt {retry_state.attempt_number}/3)...")
|
| 503 |
+
)
|
| 504 |
+
def verify_and_open_video(video_path):
|
| 505 |
+
"""Verify file existence and readability, then open with cv2.VideoCapture"""
|
| 506 |
+
if not os.path.exists(video_path):
|
| 507 |
+
raise FileNotFoundError(f"Temporary video file not found: {video_path}")
|
| 508 |
+
|
| 509 |
+
file_size = os.path.getsize(video_path)
|
| 510 |
+
if file_size == 0:
|
| 511 |
+
raise ValueError(f"Temporary video file is empty: {video_path}")
|
| 512 |
+
|
| 513 |
+
with open(video_path, "rb") as f:
|
| 514 |
+
f.read(1) # Test read access
|
| 515 |
+
|
| 516 |
+
cap = cv2.VideoCapture(video_path)
|
| 517 |
+
if not cap.isOpened():
|
| 518 |
+
raise ValueError("Could not open video file. Ensure the video format is supported (e.g., MP4) and FFmpeg is installed.")
|
| 519 |
+
|
| 520 |
+
return cap
|
| 521 |
+
|
| 522 |
+
def process_video(video_data, temp_dir):
|
| 523 |
"""Process video to detect safety violations"""
|
| 524 |
+
video_path = None
|
| 525 |
+
output_dir = os.path.join(temp_dir, "output")
|
| 526 |
+
os.makedirs(output_dir, exist_ok=True)
|
| 527 |
+
os.environ['YOLO_CONFIG_DIR'] = temp_dir # Set YOLO config dir for this process
|
| 528 |
+
|
| 529 |
try:
|
| 530 |
# Validate video data
|
| 531 |
if not video_data:
|
| 532 |
raise ValueError("Empty video data provided.")
|
| 533 |
|
|
|
|
| 534 |
logger.info(f"Received video data size: {len(video_data)} bytes")
|
| 535 |
if len(video_data) == 0:
|
| 536 |
raise ValueError("Video data is empty.")
|
| 537 |
|
| 538 |
+
# Save video to a temporary file
|
| 539 |
+
with tempfile.NamedTemporaryFile(suffix=".mp4", dir=temp_dir, delete=False) as temp_file:
|
| 540 |
temp_file.write(video_data)
|
| 541 |
+
temp_file.flush()
|
| 542 |
video_path = temp_file.name
|
| 543 |
logger.info(f"Video saved to temporary file: {video_path}")
|
| 544 |
|
|
|
|
| 550 |
raise ValueError(f"Temporary video file is empty: {video_path}")
|
| 551 |
logger.info(f"Temporary video file size: {file_size} bytes")
|
| 552 |
|
| 553 |
+
# Open video with OpenCV (with retries)
|
| 554 |
+
cap = verify_and_open_video(video_path)
|
| 555 |
+
logger.info(f"Successfully opened video file: {video_path}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 556 |
|
| 557 |
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
|
| 558 |
fps = cap.get(cv2.CAP_PROP_FPS) or 30
|
|
|
|
| 561 |
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
|
| 562 |
logger.info(f"Video properties: {duration:.2f}s, {total_frames} frames, {fps:.1f} FPS, {width}x{height}")
|
| 563 |
|
|
|
|
| 564 |
if total_frames <= 0:
|
| 565 |
raise ValueError("Video has no frames.")
|
| 566 |
|
|
|
|
| 605 |
logger.info("No more frames to process.")
|
| 606 |
break
|
| 607 |
|
|
|
|
| 608 |
try:
|
| 609 |
results = model(batch_frames, device=device, conf=0.1, verbose=False)
|
| 610 |
except Exception as e:
|
|
|
|
| 667 |
detection = {
|
| 668 |
"worker_id": worker_id,
|
| 669 |
"violation": label,
|
| 670 |
+
"confidence": round(float(conf), 2),
|
| 671 |
"bounding_box": bbox,
|
| 672 |
"timestamp": current_time
|
| 673 |
}
|
|
|
|
| 686 |
)
|
| 687 |
|
| 688 |
snapshot_filename = f"violation_{label}_worker{worker_id}_{int(current_time*100)}.jpg"
|
| 689 |
+
snapshot_path = os.path.join(output_dir, snapshot_filename)
|
| 690 |
|
| 691 |
cv2.imwrite(
|
| 692 |
snapshot_path,
|
|
|
|
| 700 |
"timestamp": current_time,
|
| 701 |
"snapshot_path": snapshot_path,
|
| 702 |
"snapshot_url": f"{CONFIG['PUBLIC_URL_BASE']}{snapshot_filename}",
|
| 703 |
+
"confidence": round(float(conf), 2)
|
| 704 |
})
|
| 705 |
|
| 706 |
logger.info(f"Captured snapshot for {label} violation by worker {worker_id} at {current_time:.2f}s")
|
| 707 |
|
|
|
|
| 708 |
cap.release()
|
|
|
|
| 709 |
processing_time = time.time() - start_time
|
| 710 |
logger.info(f"Processing complete in {processing_time:.2f}s")
|
| 711 |
|
|
|
|
| 712 |
logger.info(f"Snapshots: {snapshots}")
|
| 713 |
|
| 714 |
violations = []
|
| 715 |
for worker_id, worker_violations in unique_violations.items():
|
| 716 |
for label, detection_time in worker_violations.items():
|
|
|
|
| 717 |
confidence = next(
|
| 718 |
(float(s["confidence"]) for s in snapshots if s["worker_id"] == worker_id and s["violation"] == label),
|
| 719 |
0.0
|
|
|
|
| 726 |
}
|
| 727 |
violations.append(violation)
|
| 728 |
|
|
|
|
| 729 |
logger.info(f"Violations: {violations}")
|
| 730 |
|
| 731 |
if not violations:
|
|
|
|
| 734 |
return
|
| 735 |
|
| 736 |
score = calculate_safety_score(violations)
|
| 737 |
+
pdf_path, pdf_url, pdf_file = generate_violation_pdf(violations, score, output_dir)
|
| 738 |
|
|
|
|
| 739 |
record_id, final_pdf_url = push_report_to_salesforce(violations, score, pdf_path, pdf_file)
|
| 740 |
|
|
|
|
| 741 |
violation_table = "| Violation | Worker ID | Time (s) | Confidence |\n"
|
| 742 |
violation_table += "|-----------|-----------|----------|------------|\n"
|
| 743 |
|
|
|
|
| 745 |
display_name = CONFIG["DISPLAY_NAMES"].get(v.get("violation", "Unknown"), "Unknown")
|
| 746 |
worker_id = v.get("worker_id", "Unknown")
|
| 747 |
timestamp = v.get("timestamp", 0.0)
|
|
|
|
| 748 |
try:
|
| 749 |
confidence = float(v.get("confidence", 0.0))
|
| 750 |
except (ValueError, TypeError) as e:
|
|
|
|
| 777 |
logger.error(f"Error processing video: {str(e)}", exc_info=True)
|
| 778 |
yield f"Error processing video: {str(e)}", "", "", "", ""
|
| 779 |
finally:
|
|
|
|
| 780 |
if video_path and os.path.exists(video_path):
|
| 781 |
try:
|
| 782 |
os.remove(video_path)
|
| 783 |
logger.info(f"Cleaned up temporary video file: {video_path}")
|
| 784 |
except Exception as e:
|
| 785 |
logger.error(f"Failed to clean up temporary video file {video_path}: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 786 |
|
| 787 |
def gradio_interface(video_file):
|
| 788 |
"""Gradio interface for the video processing"""
|
| 789 |
+
temp_dir = None
|
| 790 |
+
local_video_path = None
|
|
|
|
| 791 |
try:
|
| 792 |
+
if not video_file:
|
| 793 |
+
return "No file uploaded.", "", "No file uploaded.", "", ""
|
| 794 |
+
|
| 795 |
+
# Create a unique temporary directory for this video
|
| 796 |
+
temp_dir = tempfile.mkdtemp(prefix="Ultralytics_")
|
| 797 |
+
logger.info(f"Created temporary directory for video processing: {temp_dir}")
|
| 798 |
+
|
| 799 |
+
# Copy Gradio's video file to a local temporary file
|
| 800 |
with open(video_file, "rb") as f:
|
| 801 |
video_data = f.read()
|
| 802 |
+
logger.info(f"Read Gradio video file: {video_file}, size: {len(video_data)} bytes")
|
| 803 |
+
|
|
|
|
| 804 |
if len(video_data) == 0:
|
| 805 |
return "Uploaded video file is empty.", "", "", "", ""
|
| 806 |
|
| 807 |
+
# Save to a local temporary file to avoid Gradio file deletion
|
| 808 |
+
with tempfile.NamedTemporaryFile(suffix=".mp4", dir=temp_dir, delete=False) as temp_file:
|
| 809 |
+
temp_file.write(video_data)
|
| 810 |
+
temp_file.flush()
|
| 811 |
+
local_video_path = temp_file.name
|
| 812 |
+
logger.info(f"Copied Gradio video to local temporary file: {local_video_path}")
|
| 813 |
+
|
| 814 |
if not FFMPEG_AVAILABLE:
|
| 815 |
return "FFmpeg is not available in the environment. Please install FFmpeg to process videos.", "", "", "", ""
|
| 816 |
|
| 817 |
+
for status, score, snapshots_text, record_id, details_url in process_video(video_data, temp_dir):
|
|
|
|
| 818 |
yield status, score, snapshots_text, record_id, details_url
|
| 819 |
|
| 820 |
except Exception as e:
|
| 821 |
logger.error(f"Error in Gradio interface: {e}", exc_info=True)
|
| 822 |
yield f"Error: {str(e)}", "", "Error in processing.", "", ""
|
| 823 |
+
finally:
|
| 824 |
+
# Clean up the local temporary video file
|
| 825 |
+
if local_video_path and os.path.exists(local_video_path):
|
| 826 |
+
try:
|
| 827 |
+
os.remove(local_video_path)
|
| 828 |
+
logger.info(f"Cleaned up local temporary video file: {local_video_path}")
|
| 829 |
+
except Exception as e:
|
| 830 |
+
logger.error(f"Failed to clean up local temporary video file {local_video_path}: {e}")
|
| 831 |
+
|
| 832 |
+
# Clean up the temporary directory
|
| 833 |
+
if temp_dir and os.path.exists(temp_dir):
|
| 834 |
+
shutil.rmtree(temp_dir, ignore_errors=True)
|
| 835 |
+
logger.info(f"Cleaned up temporary directory: {temp_dir}")
|
| 836 |
|
| 837 |
# ========================== # Gradio Interface # ==========================
|
| 838 |
interface = gr.Interface(
|