Automatically Organize Your Downloads Folder with Python
Your Downloads folder is a mess. PDFs mixed with images, spreadsheets buried under zip files, and that important document you downloaded last week? Good luck finding it. You've probably cleaned it up manually a dozen times, only to watch it descend back into chaos within days.
What if a Python script could do this for you—automatically, every time you run it?
Today, we're building a file organizer that sorts your downloads by file type. No more manual cleanup. No more searching through hundreds of files. Just organized folders, automatically.
What You'll Learn
- How to work with files and directories in Python
- Using the
pathlibmodule for modern file operations - Organizing code with functions for reusability
- Error handling for real-world reliability
Prerequisites
- Python 3.8 or higher
- No additional libraries required (we're using built-in modules!)
- Access to your Downloads folder
The Problem
Every day, files accumulate in your Downloads folder:
- Browser downloads
- Email attachments you save
- Files from messaging apps
- Screenshots and images
- Documents from various sources
Within a week, you have hundreds of files with no organization. Finding anything requires scrolling through a chaotic list, and the visual clutter is mentally draining.
The Solution
We'll create a Python script that:
- Scans your Downloads folder
- Identifies each file's type by extension
- Creates organized subfolders (Documents, Images, Videos, etc.)
- Moves each file to its appropriate folder
- Handles duplicates gracefully
Step 1: Setting Up File Type Categories
First, we need to define what types of files we're dealing with and where they should go:
1# Define file categories and their extensions
2FILE_CATEGORIES = {
3 "Documents": [".pdf", ".doc", ".docx", ".txt", ".rtf", ".odt", ".xls", ".xlsx", ".ppt", ".pptx", ".csv"],
4 "Images": [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".svg", ".webp", ".ico", ".tiff"],
5 "Videos": [".mp4", ".avi", ".mkv", ".mov", ".wmv", ".flv", ".webm"],
6 "Audio": [".mp3", ".wav", ".flac", ".aac", ".ogg", ".wma", ".m4a"],
7 "Archives": [".zip", ".rar", ".7z", ".tar", ".gz", ".bz2"],
8 "Code": [".py", ".js", ".html", ".css", ".java", ".cpp", ".c", ".json", ".xml"],
9 "Executables": [".exe", ".msi", ".dmg", ".app", ".deb", ".rpm"],
10}This dictionary maps folder names to lists of file extensions. When we encounter a .pdf file, we know it belongs in "Documents".
Step 2: Creating the Category Lookup Function
We need a function that takes a file extension and returns the appropriate category:
1def get_category(file_extension):
2 """
3 Determine which category a file belongs to based on its extension.
4
5 Args:
6 file_extension: The file extension (e.g., '.pdf')
7
8 Returns:
9 The category name (e.g., 'Documents') or 'Other' if not recognized
10 """
11 # Convert to lowercase for consistent matching
12 ext = file_extension.lower()
13
14 # Search through categories for a match
15 for category, extensions in FILE_CATEGORIES.items():
16 if ext in extensions:
17 return category
18
19 # If no match found, put in 'Other' folder
20 return "Other"This function handles unknown file types by putting them in an "Other" folder, so nothing gets lost.
Step 3: Handling Duplicate Files
What happens if you download report.pdf twice? We need to handle this gracefully:
1def get_unique_filename(destination_folder, filename):
2 """
3 Generate a unique filename if the file already exists.
4
5 Args:
6 destination_folder: Path object for the destination
7 filename: Original filename
8
9 Returns:
10 A unique filename (original or with a number suffix)
11 """
12 from pathlib import Path
13
14 destination = destination_folder / filename
15
16 # If file doesn't exist, use original name
17 if not destination.exists():
18 return filename
19
20 # Split filename into name and extension
21 stem = Path(filename).stem # 'report' from 'report.pdf'
22 suffix = Path(filename).suffix # '.pdf'
23
24 # Try adding numbers until we find a unique name
25 counter = 1
26 while True:
27 new_filename = f"{stem}_{counter}{suffix}"
28 if not (destination_folder / new_filename).exists():
29 return new_filename
30 counter += 1This creates filenames like report_1.pdf, report_2.pdf when duplicates are found.
Step 4: The Main Organization Function
Now we bring it all together:
1def organize_folder(source_folder):
2 """
3 Organize all files in the source folder into categorized subfolders.
4
5 Args:
6 source_folder: Path to the folder to organize (e.g., Downloads)
7
8 Returns:
9 Dictionary with counts of files moved per category
10 """
11 from pathlib import Path
12 import shutil
13
14 source = Path(source_folder)
15
16 # Verify the folder exists
17 if not source.exists():
18 print(f"Error: Folder '{source_folder}' does not exist!")
19 return {}
20
21 # Track how many files we move
22 moved_counts = {}
23
24 # Process each file in the folder
25 for item in source.iterdir():
26 # Skip directories - we only want files
27 if item.is_dir():
28 continue
29
30 # Skip hidden files (starting with .)
31 if item.name.startswith('.'):
32 continue
33
34 # Determine the category for this file
35 category = get_category(item.suffix)
36
37 # Create the category folder if it doesn't exist
38 category_folder = source / category
39 category_folder.mkdir(exist_ok=True)
40
41 # Get a unique filename to avoid overwriting
42 unique_name = get_unique_filename(category_folder, item.name)
43 destination = category_folder / unique_name
44
45 # Move the file
46 try:
47 shutil.move(str(item), str(destination))
48
49 # Update our count
50 moved_counts[category] = moved_counts.get(category, 0) + 1
51 print(f"Moved: {item.name} -> {category}/{unique_name}")
52
53 except Exception as e:
54 print(f"Error moving {item.name}: {e}")
55
56 return moved_countsThe Complete Script
1#!/usr/bin/env python3
2"""
3File Organizer - Automatically sort files into categorized folders.
4Author: Alex Rodriguez
5
6This script organizes files in a folder (like Downloads) by moving them
7into subfolders based on their file type (Documents, Images, Videos, etc.).
8"""
9
10import shutil
11from pathlib import Path
12
13
14# Define file categories and their extensions
15FILE_CATEGORIES = {
16 "Documents": [".pdf", ".doc", ".docx", ".txt", ".rtf", ".odt", ".xls", ".xlsx", ".ppt", ".pptx", ".csv"],
17 "Images": [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".svg", ".webp", ".ico", ".tiff"],
18 "Videos": [".mp4", ".avi", ".mkv", ".mov", ".wmv", ".flv", ".webm"],
19 "Audio": [".mp3", ".wav", ".flac", ".aac", ".ogg", ".wma", ".m4a"],
20 "Archives": [".zip", ".rar", ".7z", ".tar", ".gz", ".bz2"],
21 "Code": [".py", ".js", ".html", ".css", ".java", ".cpp", ".c", ".json", ".xml"],
22 "Executables": [".exe", ".msi", ".dmg", ".app", ".deb", ".rpm"],
23}
24
25
26def get_category(file_extension):
27 """
28 Determine which category a file belongs to based on its extension.
29
30 Args:
31 file_extension: The file extension (e.g., '.pdf')
32
33 Returns:
34 The category name (e.g., 'Documents') or 'Other' if not recognized
35 """
36 ext = file_extension.lower()
37
38 for category, extensions in FILE_CATEGORIES.items():
39 if ext in extensions:
40 return category
41
42 return "Other"
43
44
45def get_unique_filename(destination_folder, filename):
46 """
47 Generate a unique filename if the file already exists.
48
49 Args:
50 destination_folder: Path object for the destination
51 filename: Original filename
52
53 Returns:
54 A unique filename (original or with a number suffix)
55 """
56 destination = destination_folder / filename
57
58 if not destination.exists():
59 return filename
60
61 stem = Path(filename).stem
62 suffix = Path(filename).suffix
63
64 counter = 1
65 while True:
66 new_filename = f"{stem}_{counter}{suffix}"
67 if not (destination_folder / new_filename).exists():
68 return new_filename
69 counter += 1
70
71
72def organize_folder(source_folder):
73 """
74 Organize all files in the source folder into categorized subfolders.
75
76 Args:
77 source_folder: Path to the folder to organize (e.g., Downloads)
78
79 Returns:
80 Dictionary with counts of files moved per category
81 """
82 source = Path(source_folder)
83
84 if not source.exists():
85 print(f"Error: Folder '{source_folder}' does not exist!")
86 return {}
87
88 print(f"\nOrganizing files in: {source}")
89 print("-" * 50)
90
91 moved_counts = {}
92
93 for item in source.iterdir():
94 if item.is_dir():
95 continue
96
97 if item.name.startswith('.'):
98 continue
99
100 category = get_category(item.suffix)
101
102 category_folder = source / category
103 category_folder.mkdir(exist_ok=True)
104
105 unique_name = get_unique_filename(category_folder, item.name)
106 destination = category_folder / unique_name
107
108 try:
109 shutil.move(str(item), str(destination))
110 moved_counts[category] = moved_counts.get(category, 0) + 1
111 print(f" ✓ {item.name} -> {category}/")
112 except PermissionError:
113 print(f" ✗ {item.name} - File is in use, skipped")
114 except Exception as e:
115 print(f" ✗ {item.name} - Error: {e}")
116
117 return moved_counts
118
119
120def print_summary(moved_counts):
121 """Print a summary of the organization results."""
122 if not moved_counts:
123 print("\nNo files were moved.")
124 return
125
126 print("\n" + "=" * 50)
127 print("ORGANIZATION COMPLETE!")
128 print("=" * 50)
129
130 total = sum(moved_counts.values())
131
132 for category, count in sorted(moved_counts.items()):
133 print(f" {category}: {count} file(s)")
134
135 print(f"\nTotal files organized: {total}")
136
137
138def main():
139 """Main entry point for the file organizer."""
140 import os
141
142 # Default to Downloads folder
143 # Adjust this path for your system
144 if os.name == 'nt': # Windows
145 downloads_folder = Path.home() / "Downloads"
146 else: # macOS/Linux
147 downloads_folder = Path.home() / "Downloads"
148
149 # You can also specify a custom folder
150 # downloads_folder = Path("/path/to/your/folder")
151
152 print("=" * 50)
153 print("FILE ORGANIZER")
154 print("=" * 50)
155
156 # Run the organizer
157 moved_counts = organize_folder(downloads_folder)
158
159 # Show results
160 print_summary(moved_counts)
161
162
163if __name__ == "__main__":
164 main()How to Run This Script
-
Save the script as
file_organizer.pyon your computer -
Open your terminal (Command Prompt on Windows, Terminal on macOS/Linux)
-
Navigate to where you saved the script:
bash1cd /path/to/script -
Run the script:
bash1python file_organizer.py -
Expected output:
Prompt================================================== FILE ORGANIZER ================================================== Organizing files in: /Users/yourname/Downloads -------------------------------------------------- ✓ quarterly_report.pdf -> Documents/ ✓ vacation_photo.jpg -> Images/ ✓ meeting_recording.mp4 -> Videos/ ✓ project_backup.zip -> Archives/ ✓ script.py -> Code/ ================================================== ORGANIZATION COMPLETE! ================================================== Archives: 1 file(s) Code: 1 file(s) Documents: 1 file(s) Images: 1 file(s) Videos: 1 file(s) Total files organized: 5
Customization Options
Organize a Different Folder
Change the folder path in the main() function:
1downloads_folder = Path("/path/to/any/folder")Add New File Categories
Add entries to the FILE_CATEGORIES dictionary:
1FILE_CATEGORIES = {
2 # ... existing categories ...
3 "Fonts": [".ttf", ".otf", ".woff", ".woff2"],
4 "3D Models": [".obj", ".fbx", ".stl", ".blend"],
5}Organize by Date Instead
Modify the category logic to use file dates:
1from datetime import datetime
2
3def get_date_category(file_path):
4 """Organize by month and year."""
5 mod_time = file_path.stat().st_mtime
6 date = datetime.fromtimestamp(mod_time)
7 return date.strftime("%Y-%m") # Returns "2025-11"Skip Certain Files
Add file patterns to skip:
1SKIP_PATTERNS = ['.crdownload', '.part', '.tmp']
2
3# In the organize_folder function:
4if any(item.suffix.lower() == pattern for pattern in SKIP_PATTERNS):
5 continueCommon Issues & Solutions
| Issue | Solution |
|---|---|
| "Permission denied" error | Close any programs using the files, or run as administrator |
| Files not moving | Check that the source path is correct and exists |
| Wrong category assigned | Add the extension to the correct category in FILE_CATEGORIES |
| Script can't find Downloads | Verify the path matches your system (check Windows vs Mac paths) |
| Duplicate handling not working | Ensure you have write permissions in the destination folder |
Taking It Further
Auto-Run on Schedule
Windows Task Scheduler:
- Open Task Scheduler
- Create Basic Task
- Set trigger (daily, at startup, etc.)
- Action: Start a program
- Program:
python - Arguments:
C:\path\to\file_organizer.py
macOS/Linux (cron):
1# Run every day at 9 AM
20 9 * * * /usr/bin/python3 /path/to/file_organizer.pyAdd Logging
Track what's been organized over time:
1import logging
2
3logging.basicConfig(
4 filename='organizer.log',
5 level=logging.INFO,
6 format='%(asctime)s - %(message)s'
7)
8
9# Replace print statements with:
10logging.info(f"Moved: {item.name} -> {category}/")Create an Undo Feature
Save a manifest of moves to reverse them if needed:
1import json
2
3moves = []
4
5# After each successful move:
6moves.append({
7 "original": str(item),
8 "new": str(destination)
9})
10
11# Save at the end:
12with open("last_organization.json", "w") as f:
13 json.dump(moves, f, indent=2)Conclusion
You've just built a practical Python tool that solves a real problem. Every time your Downloads folder gets cluttered, you're one command away from perfect organization.
The beauty of this script is its simplicity—it uses only Python's built-in modules, handles edge cases gracefully, and is easy to customize for your specific needs.
Start with the basic version, then modify it as you learn what works best for your workflow. Maybe you want different categories, or date-based organization, or automatic scheduling. The foundation is here—make it yours.
Your Downloads folder will never be the same.
Sponsored Content
Interested in advertising? Reach automation professionals through our platform.