Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ A powerful and intelligent command-line utility for finding and cleaning unneces
- ⚡ **Fast Scanning**: Efficient directory traversal with configurable depth limits
- 🎨 **Clean Output**: Well-formatted terminal output with progress indicators
- 📊 **Progress Tracking**: Real-time progress bars with ETA for long operations
- 📝 **Log File**: Detailed logs of cleanup activity saved to `~/.macsweep.log`

## Categories Detected

Expand Down Expand Up @@ -83,6 +84,7 @@ Options:
--clean-downloads Interactive Downloads cleanup with format selection
--organize-downloads Organize Downloads files into separate folders by category
--no-progress Disable progress bars for minimal output
--log-file PATH Write log output to the specified file (default: ~/.macsweep.log)
--help Show help message
```

Expand Down Expand Up @@ -190,7 +192,7 @@ When you run the script, you'll see:
- **Confirmation Prompts**: Always asks before deleting files
- **Safe Categorization**: Only suggests files that are typically safe to delete
- **Error Handling**: Gracefully handles permission errors and missing files
- **Detailed Logging**: Shows exactly what was deleted (in verbose mode)
- **Detailed Logging**: Logs all cleanup actions to a file for troubleshooting

## System Requirements

Expand Down
41 changes: 37 additions & 4 deletions macsweep.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import argparse
import shutil
import time
import logging
from datetime import datetime, timedelta
from pathlib import Path
from typing import List, Dict, Tuple, Optional
Expand All @@ -18,6 +19,19 @@
import threading
import queue

logger = logging.getLogger("MacSweep")


def setup_logger(log_file: str):
"""Configure logging to file"""
log_file_path = os.path.expanduser(log_file)
logging.basicConfig(
filename=log_file_path,
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s"
)
logger.info("Logging initialized")

class ProgressBar:
"""Simple progress bar for terminal output"""

Expand Down Expand Up @@ -433,6 +447,9 @@ def cleanup_files(self, files: List[str]) -> Tuple[int, int]:
size = os.path.getsize(file_path)
if not self.dry_run:
os.remove(file_path)
logger.info(f"Removed file: {file_path} ({self.format_size(size)})")
else:
logger.info(f"[Dry Run] Would remove file: {file_path} ({self.format_size(size)})")
if self.verbose:
print(f"Removed file: {file_path} ({self.format_size(size)})")
files_removed += 1
Expand All @@ -441,15 +458,20 @@ def cleanup_files(self, files: List[str]) -> Tuple[int, int]:
size = self.get_directory_size(file_path)
if not self.dry_run:
shutil.rmtree(file_path)
logger.info(f"Removed directory: {file_path} ({self.format_size(size)})")
else:
logger.info(f"[Dry Run] Would remove directory: {file_path} ({self.format_size(size)})")
if self.verbose:
print(f"Removed directory: {file_path} ({self.format_size(size)})")
files_removed += 1
bytes_freed += size
except (OSError, IOError) as e:
logger.error(f"Error removing {file_path}: {e}")
if self.verbose:
print(f"Error removing {file_path}: {e}")
continue

logger.info(f"Cleanup finished. Files removed: {files_removed}, Space freed: {self.format_size(bytes_freed)}")
return files_removed, bytes_freed

def get_directory_size(self, path: str) -> int:
Expand Down Expand Up @@ -982,8 +1004,14 @@ def main():
help="Organize Downloads files into separate folders by category")
parser.add_argument("--no-progress", action="store_true",
help="Disable progress bars for minimal output")
parser.add_argument("--log-file", default="~/.macsweep.log",
help="Write detailed log to the specified file (default: ~/.macsweep.log)")

args = parser.parse_args()

# Initialize logging
setup_logger(args.log_file)
logger.info("MacSweep started")

# Validate path
if not os.path.exists(args.path):
Expand All @@ -998,9 +1026,11 @@ def main():
print("="*60)
print(f"Scanning: {args.path}")
print(f"Max depth: {args.depth}")
logger.info(f"Scanning path: {args.path}, depth: {args.depth}")

if args.dry_run:
print("🔍 DRY RUN MODE - No files will be deleted")
logger.info("Dry run mode enabled")

# Initialize components
scanner = FileScanner()
Expand Down Expand Up @@ -1066,11 +1096,12 @@ def main():
start_time = time.time()

files_removed, bytes_freed = ui.cleanup_engine.cleanup_files(file_paths)

cleanup_time = time.time() - start_time
print(f"\n✅ Downloads cleanup completed in {cleanup_time:.2f} seconds")
print(f"Files removed: {files_removed}")
print(f"Space freed: {ui.cleanup_engine.format_size(bytes_freed)}")
logger.info(f"Downloads cleanup completed in {cleanup_time:.2f}s. Files removed: {files_removed}. Space freed: {ui.cleanup_engine.format_size(bytes_freed)}")

if args.dry_run:
print("\n💡 This was a dry run. No files were actually deleted.")
Expand Down Expand Up @@ -1136,11 +1167,12 @@ def main():
start_time = time.time()

organized_stats = scanner.organize_downloads_files(format_analysis, show_progress=not args.no_progress)

organization_time = time.time() - start_time
if not args.no_progress:
print(f"Organization completed in {organization_time:.2f} seconds")

logger.info(f"Downloads organization completed in {organization_time:.2f}s")

# Display results
ui.display_organization_results(organized_stats)

Expand Down Expand Up @@ -1202,11 +1234,12 @@ def main():
start_time = time.time()

files_removed, bytes_freed = ui.cleanup_engine.cleanup_files(selected_files)

cleanup_time = time.time() - start_time
print(f"\n✅ Cleanup completed in {cleanup_time:.2f} seconds")
print(f"Files removed: {files_removed}")
print(f"Space freed: {ui.cleanup_engine.format_size(bytes_freed)}")
logger.info(f"Cleanup completed in {cleanup_time:.2f}s. Files removed: {files_removed}. Space freed: {ui.cleanup_engine.format_size(bytes_freed)}")

if args.dry_run:
print("\n💡 This was a dry run. No files were actually deleted.")
Expand Down