Build a Command-Line Tool in Python That Organizes Your Downloads Folder

Build a Command-Line Tool in Python That Organizes Your Downloads Folder

Build a Command-Line Tool in Python That Organizes Your Downloads Folder

 

Introduction

Your Downloads folder is often a dumping ground for PDFs, ZIPs, images, and all sorts of random files. Over time, it becomes a digital junk drawer. What if you could run a simple Python script that organizes this chaos automatically? In this post, we’ll build a command-line interface (CLI) tool using Python to scan your Downloads directory and sort files by type or date — all configurable via command-line arguments.

Section 1: Setting Up the CLI with Argparse

We’ll start by designing a flexible command-line interface using Python’s built-in argparse module. This will let users specify options like target directory, sorting method, and more.

import argparse

def get_args():
    parser = argparse.ArgumentParser(description='Organize Downloads folder by file type or date.')
    parser.add_argument('--path', type=str, default='~/Downloads', help='Path to the Downloads folder')
    parser.add_argument('--mode', type=str, choices=['type', 'date'], default='type', help='Sorting mode: type or date')
    parser.add_argument('--dry-run', action='store_true', help='Preview what will be moved without making changes')
    return parser.parse_args()

if __name__ == '__main__':
    args = get_args()
    print(args)

Run this script with options like python organize.py --mode date --dry-run. The dry-run flag is crucial for testing safely without moving files accidentally.

Section 2: Scanning and Categorizing Files

Next, let’s detect all files in the target folder and categorize them. We’ll use the os and pathlib modules for cross-platform file handling.

import os
from pathlib import Path

def scan_files(base_path):
    base = Path(base_path).expanduser()
    files = [f for f in base.iterdir() if f.is_file()]
    return files

def categorize_by_type(files):
    categories = {}
    for f in files:
        ext = f.suffix.lower().lstrip('.') or 'no_extension'
        categories.setdefault(ext, []).append(f)
    return categories

files = scan_files('~/Downloads')
file_groups = categorize_by_type(files)
for ext, group in file_groups.items():
    print(ext, len(group))

This code organizes files into a dictionary keyed by file type (extensions). Developers can customize this logic — for example, grouping certain extensions together like .jpg, .png, and .gif under an Images category.

Section 3: Moving Files Safely

Now that we’ve grouped files, let’s move them into subfolders. We’ll ensure that operations are idempotent and avoid overwriting existing files.

import shutil

def move_files(groups, base_path, dry_run=False):
    base = Path(base_path).expanduser()
    for category, files in groups.items():
        target_dir = base / category
        if not target_dir.exists():
            if not dry_run:
                target_dir.mkdir(parents=True)
        for f in files:
            dest = target_dir / f.name
            if dest.exists():
                print(f'Skipped: {f.name} already exists')
                continue
            if dry_run:
                print(f'[DRY RUN] Would move {f} -> {target_dir}')
            else:
                shutil.move(str(f), str(dest))
                print(f'Moved: {f.name} -> {category}/')

The above ensures no accidental overwriting and supports a dry-run mode for safety. For performance, this simple approach works well up to thousands of files.

Section 4: Adding Sorting by Date

Sorting by modification date can help organize older files into folders like 2024-01 or 2023-12. Here’s how to do it:

from datetime import datetime

def categorize_by_date(files):
    categories = {}
    for f in files:
        ts = f.stat().st_mtime
        date_folder = datetime.fromtimestamp(ts).strftime('%Y-%m')
        categories.setdefault(date_folder, []).append(f)
    return categories

By calling categorize_by_date() instead of categorize_by_type() based on args.mode, our CLI becomes flexible:

if args.mode == 'type':
    groups = categorize_by_type(files)
else:
    groups = categorize_by_date(files)
move_files(groups, args.path, args.dry_run)

Section 5: Packaging and Extending the CLI

Once the logic works, you can convert this script into a proper CLI command installed via setuptools or poetry. In setup.py:

from setuptools import setup, find_packages

setup(
    name='downloads-organizer',
    version='1.0',
    packages=find_packages(),
    entry_points={
        'console_scripts': [
            'organize-downloads=organizer.main:main'
        ],
    },
)

This lets you install your tool globally and run it with organize-downloads --mode type. You can also add logging, parallel file processing, or even integrate a configuration file for user preferences.

Conclusion

In this post, you built a Python CLI tool that automates file organization using argparse, pathlib, and some smart logic. This approach demonstrates how Python excels at automating everyday tasks, and the CLI framework makes it extensible for future enhancements. Try scheduling it with cron or Windows Task Scheduler to keep your Downloads folder organized automatically.

 

Useful links: