Path: blob/master/stream/remove_images.py
641 views
import argparse1import logging2import os3import shutil4import sys5from datetime import datetime, timedelta6from pathlib import Path78logging.basicConfig(9stream=sys.stdout,10format="%(asctime)s %(levelname)s %(name)s %(threadName)s : %(message)s",11)121314def parse_arguments():15parser = argparse.ArgumentParser(16description="Image cleanup utility: delete old images based on disk usage or age."17)18mode_group = parser.add_mutually_exclusive_group(required=True)19mode_group.add_argument(20"--usage",21action="store_true",22help="Trigger disk space cleanup based on usage thresholds.",23)24mode_group.add_argument(25"--age",26action="store_true",27help="Trigger cleanup of images older than a threshold in hours.",28)29parser.add_argument(30"--target_free",31type=int,32choices=range(1, 100),33metavar="[1-99]",34help="(Usage mode) Target free space percentage to reach after cleanup.",35)36parser.add_argument(37"--trigger_usage",38type=int,39choices=range(1, 100),40metavar="[1-99]",41help="(Usage mode) Disk usage percentage that triggers cleanup.",42)43parser.add_argument(44"--threshold",45choices=range(1, 24),46metavar="[1-23]",47help="Time duration (in hours) to remove images older than xx hours",48type=int,49)50parser.add_argument(51"--directory",52type=str,53default=".",54help="Directory to clean (default: current directory).",55)56return parser.parse_args()575859def get_disk_usage(path):60"""Returns disk usage stats: (total, used, free) in bytes."""61usage = shutil.disk_usage(path)62return usage.total, usage.used, usage.free636465def get_percentages(directory):66"""Returns disk usage (used and free) stats in percentages: (used, free) in %."""67total, used, free = get_disk_usage(directory)68if total == 0:69raise ValueError(70f"Invalid disk usage: total space is zero for directory '{directory}'"71)72return (used / total) * 100, (free / total) * 100737475def delete_image(file_path):76file_size = file_path.stat().st_size77file_path.unlink()78logging.info(f"Deleted: {file_path} ({file_size} bytes)")798081def delete_images_by_percentage(directory, target_free_percent, batch_size=10):82directory_path = Path(directory)83all_files = sorted(directory_path.rglob("*.jpg"), key=lambda p: p.stat().st_mtime)8485_, current_free_percent = get_percentages(directory)86logging.info(f"Current free space: {current_free_percent:.2f}%")8788files_deleted = 08990for file_path in all_files:91if current_free_percent >= target_free_percent:92logging.info(f"Reached target free space: {current_free_percent:.2f}%")93break94try:95delete_image(file_path)96files_deleted += 197except OSError as e:98logging.error(f"Error deleting file {file_path}: {e.strerror}")99100if files_deleted % batch_size == 0:101_, current_free_percent = get_percentages(directory)102103_, current_free_percent = get_percentages(directory)104logging.info(f"Final free space after deletion: {current_free_percent:.2f}%")105106107# Function to delete image files108def delete_images_by_age(directory, threshold_minutes):109now = datetime.now()110threshold_time = now - timedelta(minutes=threshold_minutes)111files_deleted = 0112113for file_path in Path(directory).rglob("*.jpg"):114modified_time = datetime.fromtimestamp(file_path.stat().st_mtime)115if modified_time < threshold_time:116try:117delete_image(file_path)118files_deleted += 1119except OSError as e:120logging.error(f"Error deleting images: {e.filename} - {e.strerror}")121122if files_deleted == 0:123logging.info("No file(s) to delete below the threshold time")124125126# Function to delete empty directories127def delete_empty_directories(directory):128for dir_path in reversed(list(Path(directory).rglob("*"))):129if dir_path.exists() and dir_path.is_dir():130try:131if not list(dir_path.iterdir()):132dir_path.rmdir()133except OSError as e:134logging.error(135f"Error deleting empty directories: {e.filename} - {e.strerror}"136)137138139def main():140args = parse_arguments()141directory = os.path.abspath(args.directory)142143if args.usage:144if args.target_free is None or args.trigger_usage is None:145logging.error(146"Error: --target_free and --trigger_usage are required in --usage mode."147)148return149current_used_percent, _ = get_percentages(directory)150logging.info(f"Disk usage: {current_used_percent:.2f}%")151if current_used_percent >= args.trigger_usage:152logging.info(153f"Disk usage exceeded {args.trigger_usage}%. Starting cleanup..."154)155delete_images_by_percentage(directory, args.target_free)156delete_empty_directories(directory)157else:158logging.info(159f"Disk usage is below {args.trigger_usage}%. No cleanup needed."160)161162elif args.age:163if args.threshold is None:164logging.error("Error: --threshold is required in --age mode.")165return166threshold_minutes = args.threshold * 60167delete_images_by_age(directory, threshold_minutes)168delete_empty_directories(directory)169170171if __name__ == "__main__":172main()173174175