Automatic Downloads Folder Organizer

Write a Python script that automatically sorts files into categorized folders.

Introduction

30 Second Summary

Your Downloads folder is probably a graveyard of random files. PDFs mixed with screenshots, installers buried under music files, and no hope of finding anything without scrolling for minutes.

In this project, you will write a Python script that scans any folder on your computer and automatically sorts every file into organized subfolders based on its type.

What You'll Build

Run your finished script on a cluttered folder and watch dozens of scattered files sort themselves into neat categories like Images, Documents, and Videos in seconds.

By the end of this project, you'll have:

  • A working script you can point at any folder to instantly sort every file by type into organized subfolders.
  • A dry-run preview mode that shows you exactly what will happen before any files actually move.
  • Smart duplicate handling that ensures no file ever gets overwritten when names collide.
  • Secret Mission: Add a timestamped log file that records every move, giving you a permanent record of what was organized.

Are there any prerequisites?

None. This guide walks you through installing Python and setting up everything you need from scratch. All you need is a computer with VS Code installed.

Before We Start

Before We Start...

Before we dive in, let's lock in what you're about to build and why it matters.

This is the moment to commit to the goal: a Python script that turns any messy folder into an organized directory in seconds.

Set Up Python

Before you can write any automation scripts, you need Python installed on your computer. Python is the programming language that powers your file organizer.

In this step, you will install Python using the official Python install manager, then create your project folder and first file to confirm everything works.

In this step, get ready to:
  • Install Python on your computer and verify it runs.
  • Create your project folder and your first Python file.
  • Run the file to confirm your environment is working.
Install Python using the Python install manager

The Python install manager is the official tool for installing and managing Python versions on Windows. It handles downloading, installing, and updating Python for you.

  • Press the Windows key to open your search bar.
  • Type Microsoft Store and press Enter to open it.
  • In the Microsoft Store search bar, type Python install manager.
  • Press Enter to search.
  • Click Get (or Install) on the Python install manager app.

What is the Python install manager?

The Python install manager is a lightweight tool that makes the python command available from any terminal on your machine.

When you run python for the first time, it automatically installs the latest stable version of Python. You do not need to download Python separately.

Prefer the terminal?

You can install the Python install manager from any terminal instead of using the Microsoft Store by running:

winget install 9NQ7512CXL7T

Now verify that Python is installed and working.

  • In VS Code, click Terminal in the top menu bar.
  • Click New Terminal.
  • Check your Python version by running this command:
python --version

What does this command do?

python --version asks Python to print which version is installed on your machine. If this is your first time running python, the install manager may take a moment to download the latest runtime automatically.

What do you see in the terminal output?

✔️ I see Python 3.14 or higher

You should see something like Python 3.14.6 printed. Python is ready to go.

ⓧ I see an older version

If you see a version lower than 3.14 (like 3.12 or 3.13), you have an older Python installation on your system taking priority.

  • Install the latest version through the Python install manager by running:
py install 3.14
  • Close your current terminal by clicking the trash icon in the terminal panel.
  • Open a new terminal (Terminal > New Terminal).
  • Run python --version again to confirm it now shows 3.14 or higher.

Still seeing the old version?

Click Start, search for Manage app execution aliases, and check that the aliases for Python (default) are enabled. If they already are, try disabling and re-enabling them to refresh the command.

help me fix python --version showing an old version after installing Python 3.14 with the Python install manager

ⓧ Command not found

If you see an error or the Microsoft Store opens, the Python install manager may not have finished installing.

  • Go back to the Microsoft Store and confirm the Python install manager shows as installed.
  • Close your current terminal by clicking the trash icon in the terminal panel.
  • Open a new terminal (Terminal > New Terminal).
  • Run python --version again.

The first time you run python, the install manager may take a moment to download and set up the latest Python runtime automatically.

Still not working?

Click Start, search for Manage app execution aliases, and check that the aliases for Python (default) are enabled. If they already are, try disabling and re-enabling them to refresh the command.

help me troubleshoot why python --version is not working after installing the Python install manager on Windows

Create your project folder and first file

Now that Python is working, you need a dedicated folder for your project files. You will create it on your Desktop so it is easy to find.

  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running this command:
mkdir file-organizer && cd file-organizer
  • Confirm you are inside the new folder by running:
pwd

You should see a path ending in file-organizer.

Don't see file-organizer in the path?

Make sure you pressed Enter after each command. If pwd shows your home directory, re-run both commands above one at a time.

help me figure out why my terminal is not in the file-organizer folder after running mkdir and cd

Now open this folder as your VS Code workspace so all your files live in one place.

  • In VS Code, click File in the top menu bar.
  • Click Open Folder.
  • Navigate to your Desktop, select the file-organizer folder, and click Select Folder.

You should see FILE-ORGANIZER in the Explorer sidebar on the left.

  • Open a new terminal now that your workspace is set (Terminal > New Terminal).
  • Click File in the top menu bar.
  • Click New File.
  • Save the file immediately with Ctrl+S and name it organizer.py.
  • Type the following code into the file:
print("Hello! Python is working.")

What does this code do?

print() is a built-in Python function that outputs text to the terminal. If this runs successfully, it proves Python is installed correctly and your file is set up properly.

  • Save the file with Ctrl+S.

Before you run this, what do you expect to see in the terminal?

  • Run your script by entering this command in the terminal:
python organizer.py

You should see Hello! Python is working. printed in the terminal. That confirms Python runs your code and your project is ready.

Don't see the message?

Make sure you saved organizer.py with Ctrl+S before running the command. Unsaved changes will not be picked up by Python.

Check that your terminal is inside the file-organizer folder. Run pwd to confirm the path ends with file-organizer.

help me figure out why python organizer.py is not printing anything in my VS Code terminal

✔️ Awesome, I've got everything!

Great. Double check you have saved organizer.py and that running python organizer.py prints the hello message.

ⓧ I'd like to double check the full code

Your organizer.py file should contain exactly this:

print("Hello! Python is working.")

Your environment is ready and Python is running your code. Next up, you will teach your script to scan a folder and list every file inside it.

Scan a Folder and List Its Files

Your Python environment is ready and your first script proved it works. Now you can start building the actual file organizer.

Before you can sort files into folders, your script needs a way to see what is inside a folder. Python's pathlib library gives your script the ability to scan directories and identify each file by its type.

In this step, get ready to:
  • Create a test folder with sample files to practice on.
  • Write a script that scans the folder and prints each filename with its extension.
Create a test folder with sample files

You need some files for the script to practice on. Instead of risking your real Downloads folder, you will create a dedicated test_files folder with dummy files of different types.

  • In the VS Code terminal, create your test folder by running this command:
mkdir test_files
  • Move into the new folder by running:
cd test_files
  • Create six dummy files with different extensions by running these commands one at a time:
New-Item report.pdf
New-Item photo.jpg
New-Item song.mp3
New-Item notes.txt
New-Item video.mp4
New-Item backup.zip

What do these commands do?

Each New-Item command creates an empty file with the given name and extension. The files have no real content inside them, but they have real extensions like .pdf, .jpg, and .mp3.

That is all your script needs. It reads the extension to decide which category a file belongs to, not the actual file contents.

  • Move back up to your project folder by running:
cd ..
  • Confirm the files exist by running:
ls test_files

You should see all six files listed: backup.zip, notes.txt, photo.jpg, report.pdf, song.mp3, and video.mp4.

Don't see six files?

  • Make sure you ran each New-Item command while inside the test_files folder, not in the parent file-organizer folder.
  • If you see an error about New-Item not being recognized, your terminal may be using Command Prompt instead of PowerShell. In Command Prompt, use echo. > report.pdf instead (repeat for each filename).
  • help me create test files in my terminal
Write a script that scans and lists the files

Now that the test files exist, you can write Python code that reads the folder and prints what it finds. This is the foundation your organizer will build on.

  • Switch back to organizer.py in VS Code.
  • Select all the existing code (the print("Hello! Python is working.") line) and delete it.
  • Replace it with this code that scans your test folder:
from pathlib import Path

# Point at the test_files folder
source = Path("test_files")

# Loop through every item in the folder
for item in source.iterdir():
    # Only process files, skip any subfolders
    if item.is_file():
        print(item.name, item.suffix)

What does this code do?

  • from pathlib import Path loads Python's built-in library for working with file paths.
  • Path("test_files") creates a path object pointing at your test folder.
  • .iterdir() loops through every item inside that folder.
  • .is_file() filters out subfolders so you only process actual files.
  • .name gives you the full filename (like photo.jpg) and .suffix gives just the extension (like .jpg).
  • Save organizer.py by pressing Cmd+S (macOS) or Ctrl+S (Windows).

Before you run the script, what do you think it will print? You created six files in the test_files folder, so you should expect six lines of output.

  • Run your script by entering this command in the terminal:
python organizer.py

You should see all six filenames printed with their extensions, something like:

backup.zip .zip
notes.txt .txt
photo.jpg .jpg
report.pdf .pdf
song.mp3 .mp3
video.mp4 .mp4

Seeing an error instead of filenames?

  • If you see a FileNotFoundError, make sure your terminal is in the file-organizer folder (not inside test_files). Run cd .. if you are still inside test_files.
  • If you see a SyntaxError, double-check that your indentation uses spaces (not tabs) and matches the code above exactly.
  • help me debug my file scanning script

✔️ Awesome, I've got everything!

Great. Double check you have saved organizer.py and confirm you can see six filenames printed in the terminal.

ⓧ I'd like to double check the full code

Here is the complete organizer.py at this point:

from pathlib import Path

source = Path("test_files")

for item in source.iterdir():
    if item.is_file():
        print(item.name, item.suffix)

Your script can now read any folder and identify every file's type. Next up, you will use this information to actually sort the files into organized category folders.

Sort Files Into Category Folders

Your script can see what is inside a folder. That is a useful first step, but the real payoff is automatic organization.

In this step, you will build the core sorting logic. A dictionary maps file extensions to categories, a helper function looks up each file's category, and shutil moves the file into the right subfolder.

In this step, get ready to:
  • Define a category dictionary that maps extensions to folder names.
  • Write a lookup function that returns the correct category for any file.
  • Move each file into its matching subfolder automatically.
Define your category dictionary

Your script needs a way to know which extensions belong to which category. A Python dictionary is perfect for this. Each key is a category name, and each value is a list of extensions that belong in that folder.

  • In VS Code, open organizer.py and select all of the existing code.
  • Replace the entire file contents with the following code:
from pathlib import Path
import shutil


# Map category names to their file extensions
FILE_CATEGORIES = {
    "Images": [".jpg", ".jpeg", ".png", ".gif", ".svg", ".webp", ".bmp", ".ico"],
    "Documents": [".pdf", ".docx", ".doc", ".xlsx", ".xls", ".pptx", ".ppt", ".txt", ".csv"],
    "Videos": [".mp4", ".mkv", ".avi", ".mov", ".wmv"],
    "Audio": [".mp3", ".wav", ".flac", ".aac", ".wma"],
    "Archives": [".zip", ".rar", ".7z", ".tar", ".gz"],
    "Code": [".py", ".js", ".html", ".css", ".java", ".cpp", ".c"],
    "Installers": [".exe", ".msi", ".dmg"],
}


def get_category(file_extension):
    """Return the category folder name for a given file extension."""
    for category, extensions in FILE_CATEGORIES.items():
        if file_extension.lower() in extensions:
            return category
    return "Other"

What does this code do?

FILE_CATEGORIES is a dictionary where each key is a folder name (like "Images") and each value is a list of file extensions that belong in that folder.

The get_category() function takes a file extension, loops through every category in the dictionary, and returns the matching category name. If no match is found, it returns "Other" as a catch-all.

Add the move logic

Now you have a way to look up any file's category. The next piece is the loop that actually scans the folder and moves each file into its matching subfolder.

  • In organizer.py, add the following code below the get_category() function (at the very bottom of the file):
source = Path("test_files")

# Loop through every item in the folder
for item in source.iterdir():
    if item.is_file():
        category = get_category(item.suffix)
        # Build the destination path using the / operator
        dest_folder = source / category
        dest_folder.mkdir(exist_ok=True)
        shutil.move(str(item), str(dest_folder / item.name))
        print(f"  [MOVED] {item.name} -> {category}/")

What does this code do?

For each file in the test_files folder, the loop calls get_category() to find where the file belongs. It then builds a destination path using the / operator, which joins paths together cleanly.

.mkdir(exist_ok=True) creates the category subfolder if it does not already exist. shutil.move() then moves the file into that subfolder. The print() line confirms each move so you can see the script working.

  • Save organizer.py.
Run and observe the result

Your script now has everything it needs to organize files. Before you run it, take a moment to think about what you expect to happen.

You have 6 files in test_files. Do you think each file will end up in a different folder, or will some share a folder?

  • Run the script by entering this command in your terminal:
python organizer.py

You should see output showing each file being moved into its category:

  [MOVED] backup.zip -> Archives/
  [MOVED] notes.txt -> Documents/
  [MOVED] photo.jpg -> Images/
  [MOVED] report.pdf -> Documents/
  [MOVED] song.mp3 -> Audio/
  [MOVED] video.mp4 -> Videos/

Why are two files in Documents?

Both report.pdf and notes.txt have extensions listed under the "Documents" key in your dictionary. The script groups files by type, not by name.

  • Open the test_files folder in File Explorer to confirm the subfolders were created.
  • You should see five new subfolders: Archives, Audio, Documents, Images, and Videos.
  • Open any subfolder to verify the correct files are inside.

Not seeing the subfolders?

  • Make sure you saved organizer.py before running it.
  • Check that your terminal is in the file-organizer folder, not inside test_files.
  • Verify that the test_files folder still contains files. If you already ran the script once, the files have already moved into subfolders.

Still stuck? Help me debug why my file organizer script is not creating subfolders or moving files.

✔️ Awesome, I've got everything!

Great. Double check you have saved organizer.py and confirmed the subfolders exist in test_files.

ⓧ I'd like to double check the full code

Scroll up and confirm your organizer.py contains these sections in order:

  • The imports: from pathlib import Path and import shutil.
  • The FILE_CATEGORIES dictionary with 7 categories (Images, Documents, Videos, Audio, Archives, Code, Installers).
  • The get_category() function that loops through the dictionary and returns a category name.
  • The source = Path("test_files") line followed by the for loop that moves each file.

Your script just organized an entire folder in under a second. Next up, you will make it safe to use on real files by handling duplicate filenames and adding a preview mode.

Handle Duplicates and Add Dry-Run Mode

Your sorting script moves files into the right folders, but it has a dangerous flaw. If a file with the same name already exists in the destination folder, shutil.move() raises an error and the whole script crashes.

In this step, you'll hit that error on purpose, build a fix that renames duplicates automatically, and add a dry-run mode that previews changes before anything moves.

In this step, get ready to:
  • Trigger the duplicate file error and fix it with a helper function.
  • Add a dry-run preview mode that shows what would happen without moving files.
  • Test both modes and verify duplicates get unique names.
Trigger the duplicate problem

Your test_files folder already has files organized into subfolders from the previous step. To trigger the duplicate problem, you'll add new files at the root of test_files that share names with files already inside the subfolders.

  • Create duplicate files in your test folder by running these commands:
cd test_files
echo. > notes.txt
echo. > photo.jpg
cd ..
  • Confirm the new files exist alongside the subfolders by running:
dir test_files

You should see both the flat files (notes.txt, photo.jpg) and the category subfolders (Documents, Images, etc.) listed together.

Before you run the script, think about what will happen. The script will try to move notes.txt into Documents/ where a notes.txt already lives. Do you think it will succeed or fail?

  • Run the script to see what happens:
python organizer.py

The script crashes with an error because the destination file already exists. This is exactly the flaw you need to fix.

Why does this happen?

When shutil.move() targets a path that already exists on Windows, Python raises an error instead of silently overwriting. That is safe default behavior, but it means your script needs to handle duplicates explicitly.

Write the duplicate-handling function

The fix is a helper function that checks whether the destination already exists. If it does, the function appends _1, _2, etc. until it finds a name that is not taken.

  • In organizer.py, find the blank line after the get_category() function ends (after the return "Other" line).
  • Add the following function below it:
def get_unique_path(destination):
    """If a file already exists at destination, add a number to avoid overwriting."""
    if not destination.exists():
        return destination

    counter = 1
    while True:
        new_name = f"{destination.stem}_{counter}{destination.suffix}"
        new_path = destination.parent / new_name
        if not new_path.exists():
            return new_path
        counter += 1

What does this code do?

  • destination.exists() checks if a file already lives at the target path.
  • If no conflict exists, the original path is returned unchanged.
  • If there IS a conflict, the while True loop tries notes_1.txt, notes_2.txt, etc. until it finds a name that does not exist yet.
  • destination.stem gives the filename without extension, and destination.suffix gives the extension (like .txt).
  • Save organizer.py.

Seeing an indentation error?

Make sure get_unique_path is at the same indentation level as get_category (no indent, flush left). Python is strict about spacing.

Help me fix my indentation

Add dry-run preview mode

Right now, the script moves files immediately with no way to preview what would happen. A dry-run mode lets you see the plan before committing.

You'll wrap all the logic into a single organize_folder() function that accepts a dry_run parameter.

  • In organizer.py, delete everything from the line source = Path("test_files") all the way to the end of the file.
  • Replace it with the following organize_folder() function:
def organize_folder(source_folder, dry_run=True):
    """Organize files in the source folder into category subfolders."""
    source = Path(source_folder)

    if not source.exists():
        print(f"Error: Folder '{source_folder}' does not exist!")
        return

    moved_count = 0
    skipped_count = 0

    for item in sorted(source.iterdir()):
        if not item.is_file():
            skipped_count += 1
            continue

        category = get_category(item.suffix)
        dest_folder = source / category
        dest_path = dest_folder / item.name
        dest_path = get_unique_path(dest_path)

        if dry_run:
            print(f"  [PREVIEW] {item.name} -> {category}/")
        else:
            dest_folder.mkdir(exist_ok=True)
            shutil.move(str(item), str(dest_path))
            print(f"  [MOVED] {item.name} -> {category}/")

        moved_count += 1

What does this code do?

  • dry_run=True defaults to safe mode. When True, the function prints [PREVIEW] messages but never moves files.
  • sorted(source.iterdir()) processes files in alphabetical order for consistent output.
  • get_unique_path(dest_path) ensures no file gets overwritten, even in live mode.
  • Subfolders are counted as skipped since only files should be organized.
  • Directly below the moved_count += 1 line (still inside the function, but outside the for loop), add the summary and entry point code:
    print(f"\n--- Summary ---")
    mode = "previewed" if dry_run else "moved"
    print(f"Files {mode}: {moved_count}")
    print(f"Folders skipped: {skipped_count}")


folder_to_organize = "test_files"

print("=== File Organizer ===")
print(f"Scanning: {folder_to_organize}\n")
organize_folder(folder_to_organize, dry_run=True)

What does this code do?

  • The four print lines (indented) are the summary. They sit inside organize_folder() but after the loop ends.
  • The unindented lines at the bottom are the script's entry point. They set the folder name and call organize_folder() with dry_run=True for safety.
  • Save organizer.py.

Getting an indentation error on the summary lines?

The summary print statements must be indented one level (inside organize_folder) but NOT inside the for loop. They should align with the for keyword itself.

The entry point lines (folder_to_organize, print, and the function call) must have zero indentation.

Help me fix my indentation

Test both modes

The script currently calls organize_folder() with dry_run=True, so it will only preview. Before you run it, do you think it will show the duplicate files as separate entries?

  • Run the script to see the dry-run preview:
python organizer.py

You should see [PREVIEW] messages for each file and a summary at the end. No files actually move.

What should I see?

The output lists every file in test_files with a [PREVIEW] tag and its target category folder. The summary shows the total count of files previewed and folders skipped.

Now test live mode to confirm files actually move and duplicates get renamed.

  • In organizer.py, find this line at the bottom:
organize_folder(folder_to_organize, dry_run=True)
  • Change dry_run=True to dry_run=False, so the line looks like this:
organize_folder(folder_to_organize, dry_run=False)
  • Save organizer.py and run it:
python organizer.py

You should see [MOVED] messages for each file. The duplicate files get renamed automatically (e.g., notes_1.txt).

  • Open the test_files folder in File Explorer and check the Documents subfolder.
  • You should see both notes.txt (the original from Step 3) and notes_1.txt (the duplicate, safely renamed).

Script crashes instead of renaming?

Make sure your organize_folder() function calls get_unique_path(dest_path) before the shutil.move() line. Without it, the script still tries to move to the conflicting path.

Help me debug my file organizer

  • Change dry_run=False back to dry_run=True in organizer.py so the script defaults to safe preview mode going forward.
  • Save the file.

✔️ Awesome, I've got everything!

Great. Make sure you have saved organizer.py with dry_run=True as the default.

ⓧ I'd like to double check the full code

Scroll up and confirm your organizer.py contains these sections in order:

  • Imports: from pathlib import Path and import shutil.
  • The FILE_CATEGORIES dictionary.
  • The get_category() function.
  • The get_unique_path() function.
  • The organize_folder() function with dry_run parameter, the for-loop, and the summary print lines.
  • Entry point: folder_to_organize = "test_files", two print lines, and organize_folder(folder_to_organize, dry_run=True).

Your script is now safe and smart. It previews changes, handles duplicates gracefully, and gives you a summary of what happened. This is a real automation tool you can point at any messy folder on your computer.

Secret mission

Add a Timestamped Log File

Your organizer works, but it has no memory. Every time you run it, the results vanish from the terminal. Can you add a permanent log file that records every move with timestamps, building a history across multiple runs?

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project runs entirely locally, so there are no ongoing costs.

Resources you used:

  • The file-organizer project folder (contains organizer.py, organizer_log.txt, and test_files).
  • Python 3.14 (installed via the Python install manager).

Keep everything running

No action needed. Choose this if you plan to keep using Python or want to extend the file organizer script further.

  • Your file-organizer folder stays on your machine with all your files intact.
  • Python remains available for future projects.
  • You can run python organizer.py anytime to organize files.

Pause - I'll come back to this later

No processes to stop since this is a local script, not a running server. Your files will be waiting for you whenever you return.

  • Close the VS Code window to free up memory.
  • Your file-organizer folder and Python installation stay in place.

Delete - I don't want to use this again

Remove all project files and optionally uninstall Python entirely.

  • Close VS Code if it is still open.
  • Delete the project folder by opening a terminal and running:
cd ~\Desktop
rmdir /s /q file-organizer
  • To remove Python and all its managed runtimes, open a terminal and run:
py uninstall --purge
  • Then uninstall the Python install manager itself. Click **Start**, search for **Installed apps**, find **Python install manager**, and click **Uninstall**.

Nice Work!

Nice Work!

You just built a fully functional file organizer in Python from scratch. Point it at any messy folder on your computer and it sorts everything into tidy categories in seconds.

Here's what you learned:

  • Wrote a Python script that scans any folder and identifies file types using pathlib, dictionaries, and loops.
  • Built automatic sorting logic with shutil.move() that organizes files into category subfolders.
  • Handled real-world edge cases with a duplicate-safe helper function and a dry-run preview mode so the script never moves files without your permission.
  • Secret Mission: Created a timestamped log file that records every action for a permanent record of what was organized.

Ready to quiz yourself?