Automate Your Downloads Folder

Build a Python script that auto-sorts your Downloads folder by file type.

Introduction

30 Second Summary

Everyone's Downloads folder eventually turns into a dumping ground. Files pile up with no organization, and finding anything means scrolling through hundreds of randomly named items.

In this project, you will build a Python script that automatically sorts your Downloads folder into organized subfolders by file type, with a safe preview mode and automatic scheduling.

What You'll Build

A command-line Python tool that scans your Downloads folder, categorizes every file by its extension, and moves each one into the correct subfolder.

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

  • A working file organizer script that sorts files into categories like Images, Documents, Videos, and Code.
  • A dry-run safety mode that previews exactly what the script will do before moving anything.
  • An automatic scheduler that runs the organizer at set intervals so your Downloads folder stays clean without manual effort.
  • Secret Mission: Add a date-based sorting mode that organizes files into Year/Month folders based on when they were last modified.

Are there any prerequisites?

You should have basic Python knowledge (loops, functions, and variables) and VS Code installed on your computer. No additional accounts or cloud services are needed.

Before We Start

Before We Start...

Set Up Your Environment

Before you can automate anything, you need the right tools in place. Python is the language your organizer script will run on, and pip is the package manager that installs external libraries.

In this step, you will confirm Python is ready on your machine, install the one external library you need, and create a clean project folder with the two files you will write throughout this project.

In this step, get ready to:
  • Verify Python and pip are installed and working.
  • Install the schedule library for automatic task scheduling.
  • Create your project folder and two starter files.
Open VS Code and verify Python is installed

Python is the runtime that executes your organizer script. You need version 3.7 or higher for this project.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type VS Code and press Enter to open it.
  • Open a terminal inside VS Code by clicking Terminal in the top menu bar, then selecting New Terminal.
  • Check your Python version by running this command:
python --version

Which Python command should I use?

Depending on your system, the Python command might be python, python3, or py. If python --version does not work, try the alternatives below.

Note which command works for you. You will use it throughout this project.

✔️ I see Python 3.7 or higher

You should see output like Python 3.12.4 or any version number at or above 3.7. You are all set to continue.

ⓧ I see a version lower than 3.7

Your Python installation is too old for this project. The schedule library requires Python 3.7 or higher.

  • Visit python.org/downloads and download the latest Python 3 installer for your operating system.
  • Run the installer. On Windows, make sure to check the box that says Add Python to PATH before clicking Install.
  • Close and reopen your VS Code terminal, then run the version check again.

ⓧ Command not found

Your terminal does not recognize the python command. Try these alternatives first:

python3 --version
py --version

If one of these shows a version number at or above 3.7, use that command for the rest of this project.

If none of them work, you need to install Python:

  • Visit python.org/downloads and download the latest Python 3 installer for your operating system.
  • Run the installer. On Windows, make sure to check the box that says Add Python to PATH before clicking Install.
  • Close and reopen your VS Code terminal, then run python --version (or python3 --version) again to confirm.
Verify pip and install the schedule library

pip is Python's package manager. It comes bundled with Python 3.7+ and lets you install external libraries from the Python Package Index.

  • Confirm pip is available by running this command:
pip --version

Command not found for pip?

Try pip3 --version instead. If that works, use pip3 wherever this project says pip.

If neither works, try python -m pip --version (replacing python with whichever command worked for you in the previous step).

Help me fix pip not being recognized in my terminal.

  • Install the schedule library by running this command:
pip install schedule==1.2.2

What is the schedule library?

The schedule library lets you run Python functions on a timer using a human-readable syntax like "every 30 minutes". You will use it in the final step to make your organizer run automatically.

  • Verify the installation succeeded by running this command:
python -c "import schedule; print(schedule.__version__)"
  • You should see 1.2.2 printed in the terminal.

Seeing an error when importing schedule?

Make sure you use the same Python command that worked in the previous step. If you used python3 earlier, run python3 -c "import schedule; print(schedule.__version__)" instead.

If you see a ModuleNotFoundError, pip may have installed to a different Python version. Try pip3 install schedule==1.2.2 or python3 -m pip install schedule==1.2.2.

Help me fix the schedule import error.

Create your project folder and files

Now you will create a dedicated folder for this project and the two Python files you will build throughout the remaining steps.

  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir downloads-organizer
cd downloads-organizer
  • Create the two project files by running these commands:
touch organizer.py scheduler.py
  • Confirm the files exist by running:
ls
  • You should see organizer.py and scheduler.py in the output.

Using Windows without touch?

The touch command may not exist on Windows. Instead, create the files directly in VS Code after opening the folder (described in the next bullets).

Help me create empty Python files on Windows.

  • In VS Code, click File in the top menu bar.
  • Click Open Folder.
  • Navigate to your Desktop and select the downloads-organizer folder you just created, then click Open.

You should see organizer.py and scheduler.py listed in the Explorer sidebar on the left. If you skipped the touch command on Windows, right-click in the Explorer panel and select New File to create each file manually.

What are these two files for?

organizer.py is where you will write the logic that scans your Downloads folder, categorizes files, and moves them into subfolders.

scheduler.py is a lightweight wrapper that runs your organizer automatically on a timer so your Downloads folder stays clean without manual effort.

Your environment is ready. Next up, you will write the core file organizer logic that scans your Downloads folder and sorts files by type.

Build the File Organizer Core

Your environment is ready to go. Now it's time to write the Python logic that actually sorts your files.

Every file in your Downloads folder has an extension that reveals its type. Your organizer needs a way to read those extensions, match them to categories like Images or Documents, and move each file into the correct subfolder.

In this step, get ready to:
  • Define a dictionary of file categories and their extensions.
  • Write a function that identifies which category a file belongs to.
  • Write the main organizer function with a safe dry-run mode.
Define your imports and file category rules

The organizer needs to know which file extensions belong to which category. A dictionary is the perfect structure for this. Each key is a folder name, and each value is a list of extensions that belong in that folder.

  • In VS Code, open organizer.py and add the following imports, path configuration, and category dictionary:
from pathlib import Path
import shutil

# Configure your Downloads folder path
DOWNLOADS = Path.home() / "Downloads"

# File category rules - maps folder name to list of extensions
FILE_CATEGORIES = {
    "Images": [".jpg", ".jpeg", ".png", ".gif", ".svg", ".webp"],
    "Documents": [".pdf", ".doc", ".docx", ".txt", ".rtf", ".xlsx", ".pptx"],
    "Videos": [".mp4", ".avi", ".mkv", ".mov", ".wmv"],
    "Audio": [".mp3", ".wav", ".flac", ".aac", ".ogg"],
    "Archives": [".zip", ".rar", ".7z", ".tar", ".gz"],
    "Code": [".py", ".js", ".html", ".css", ".java", ".cpp"],
}

What does this code do?

Path.home() / "Downloads" builds the full path to your Downloads folder, no matter which operating system you use. Path.home() returns your user folder (like /Users/yourname on macOS or C:\Users\yourname on Windows), and the / operator joins path segments together.

FILE_CATEGORIES is a dictionary where each key is a folder name (like "Images") and each value is a list of extensions that belong in that folder. This makes the organizer easy to customize. Want to add a new category? Just add a new key-value pair.

  • Save organizer.py.

You won't see any output yet. The next chunk builds a function that uses this configuration.

Seeing a red underline in VS Code?

If VS Code shows a squiggly line under from pathlib import Path, it may not have detected your Python interpreter. Press Cmd+Shift+P (macOS) or Ctrl+Shift+P (Windows), type Python: Select Interpreter, and choose the Python version you verified in Step 1.

help me fix a Python import error in VS Code

Write the get_category function

Now you need a function that takes a file and figures out which category it belongs to. It reads the file's extension and checks it against your dictionary.

  • Below the FILE_CATEGORIES dictionary in organizer.py, add this function:
def get_category(file_path):
    """Determine which category a file belongs to based on its extension."""
    extension = file_path.suffix.lower()
    for category, extensions in FILE_CATEGORIES.items():
        if extension in extensions:
            return category
    return "Other"

What does this code do?

file_path.suffix returns the file's extension including the dot (like .pdf or .jpg). Calling .lower() on it ensures that .PNG and .png both match the same category.

The function loops through every category in your dictionary. If it finds a match, it returns that category name immediately. If no category matches, it returns "Other" as a catch-all so no file is left behind.

  • Save organizer.py.

You won't see output yet. The next function calls get_category() for every file it finds.

Indentation errors?

Python is strict about indentation. Make sure the for loop and return statements inside the function are indented with exactly 4 spaces. Mixing tabs and spaces causes invisible bugs.

help me fix a Python indentation error in my get_category function

Write the organize_files function

This is the main engine of your script. It scans every file in your Downloads folder, determines its category, and either previews what it would do (dry run) or actually moves the file.

  • Below the get_category() function in organizer.py, add this function:
def organize_files(dry_run=True):
    """Organize files in the Downloads folder by type."""
    if not DOWNLOADS.exists():
        print(f"Error: Downloads folder not found at {DOWNLOADS}")
        return

    for file_path in DOWNLOADS.iterdir():
        if not file_path.is_file():
            continue

        category = get_category(file_path)
        destination_folder = DOWNLOADS / category
        destination = destination_folder / file_path.name

        if dry_run:
            print(f"[DRY RUN] {file_path.name} -> {category}/")
        else:
            destination_folder.mkdir(parents=True, exist_ok=True)
            shutil.move(str(file_path), str(destination))
            print(f"Moved: {file_path.name} -> {category}/")

What does this code do?

DOWNLOADS.exists() checks that the Downloads folder actually exists before trying to scan it. This prevents a confusing crash if the path is wrong.

DOWNLOADS.iterdir() loops through every item in the folder. The .is_file() check skips subdirectories so the script only processes actual files.

The dry_run parameter defaults to True, which means the script only prints what it would do. This is a safety pattern used in professional automation. You always preview before you move files for real.

shutil.move() wraps paths in str() for compatibility with Python versions before 3.9.

  • Save organizer.py.
  • Test the organizer by running this command in your terminal:
python -c "from organizer import organize_files; organize_files()"

You should see lines like [DRY RUN] report.pdf -> Documents/ for each file in your Downloads folder. No files have actually moved.

Why use python -c?

The -c flag lets you run a quick Python snippet directly from the terminal. This imports your organize_files function and calls it without needing to add a test call inside the file. In the next step, you'll add a proper command-line interface so you can run the script directly.

Seeing no output at all?

If the command runs but prints nothing, your Downloads folder might be empty or contain only subdirectories. Drop a test file (any file) into your Downloads folder and run the command again.

If you see ModuleNotFoundError: No module named 'organizer', make sure your terminal is inside the downloads-organizer folder. Run cd ~/Desktop/downloads-organizer and try again.

help me troubleshoot my organizer.py script that isn't producing any output

✔️ Awesome, I've got everything!

Great. Double check you've saved organizer.py and you can move on to adding the summary report and CLI interface.

ⓧ I'd like to double check the full code

Here's what your full organizer.py should look like at this point:

from pathlib import Path
import shutil

# Configure your Downloads folder path
DOWNLOADS = Path.home() / "Downloads"

# File category rules - maps folder name to list of extensions
FILE_CATEGORIES = {
    "Images": [".jpg", ".jpeg", ".png", ".gif", ".svg", ".webp"],
    "Documents": [".pdf", ".doc", ".docx", ".txt", ".rtf", ".xlsx", ".pptx"],
    "Videos": [".mp4", ".avi", ".mkv", ".mov", ".wmv"],
    "Audio": [".mp3", ".wav", ".flac", ".aac", ".ogg"],
    "Archives": [".zip", ".rar", ".7z", ".tar", ".gz"],
    "Code": [".py", ".js", ".html", ".css", ".java", ".cpp"],
}


def get_category(file_path):
    """Determine which category a file belongs to based on its extension."""
    extension = file_path.suffix.lower()
    for category, extensions in FILE_CATEGORIES.items():
        if extension in extensions:
            return category
    return "Other"


def organize_files(dry_run=True):
    """Organize files in the Downloads folder by type."""
    if not DOWNLOADS.exists():
        print(f"Error: Downloads folder not found at {DOWNLOADS}")
        return

    for file_path in DOWNLOADS.iterdir():
        if not file_path.is_file():
            continue

        category = get_category(file_path)
        destination_folder = DOWNLOADS / category
        destination = destination_folder / file_path.name

        if dry_run:
            print(f"[DRY RUN] {file_path.name} -> {category}/")
        else:
            destination_folder.mkdir(parents=True, exist_ok=True)
            shutil.move(str(file_path), str(destination))
            print(f"Moved: {file_path.name} -> {category}/")

Your organizer can scan files and preview where they'd go. Next up, you'll add a summary report showing totals per category and a proper command-line interface so you can run the script directly.

Add Summary Report and Execute Mode

Your organizer can already scan your Downloads folder and preview where files would go. But a list of moves without a summary is hard to digest at a glance.

In this step, you will add a summary report that shows how many files land in each category, plus a command-line flag that switches from safe preview mode to actually moving files. This gives you full control over when the script acts for real.

In this step, get ready to:
  • Add a counter that tracks how many files go into each category.
  • Print a formatted summary report after processing.
  • Add a command-line interface so you can switch between dry-run and execute mode.
Add a summary counter to the organizer

Right now your script prints each file move but never tells you the totals. A dictionary can count files per category as the loop runs, then print the results at the end.

  • In organizer.py, find your organize_files function and replace it with this updated version:
def organize_files(dry_run=True):
    """Organize files in the Downloads folder by type."""
    if not DOWNLOADS.exists():
        print(f"Error: Downloads folder not found at {DOWNLOADS}")
        return

    counts = {}
    total = 0

    for file_path in DOWNLOADS.iterdir():
        if not file_path.is_file():
            continue

        category = get_category(file_path)
        destination_folder = DOWNLOADS / category
        destination = destination_folder / file_path.name

        # Update counts
        counts[category] = counts.get(category, 0) + 1
        total += 1

        if dry_run:
            print(f"[DRY RUN] {file_path.name} -> {category}/")
        else:
            destination_folder.mkdir(parents=True, exist_ok=True)
            shutil.move(str(file_path), str(destination))
            print(f"Moved: {file_path.name} -> {category}/")

    # Print summary report
    print("\n--- Summary ---")
    for category, count in sorted(counts.items()):
        print(f"  {category}: {count} files")
    print(f"  Total: {total} files")

    if dry_run and total > 0:
        print("\nThis was a dry run. Add --run to actually move files.")

What does this code do?

counts = {} creates an empty dictionary that will map each category name to the number of files found in that category. The .get(category, 0) + 1 pattern safely increments the count even if the key doesn't exist yet.

After the loop finishes, the summary block prints each category alphabetically with its file count, then the grand total. The final hint line only appears during a dry run, reminding you how to execute for real.

  • Save organizer.py.

Seeing a syntax error?

Make sure you replaced the entire organize_files function, including the def line. If you accidentally left the old function body below the new one, Python will raise an indentation or syntax error.

help me fix a syntax error in my organize_files function

Add a command-line interface

Python's sys module gives your script access to command-line arguments. You will check whether the user passed a --run flag to decide between dry-run and execute mode.

  • In organizer.py, find the import lines at the top of the file:
from pathlib import Path
import shutil
  • Add import sys directly below import shutil, so the imports now look like this:
from pathlib import Path
import shutil
import sys
  • Now scroll to the very bottom of organizer.py (below the organize_files function) and add this code:
if __name__ == "__main__":
    run_mode = "--run" in sys.argv

    if run_mode:
        print("Organizing files for real...")
        organize_files(dry_run=False)
    else:
        print("Previewing changes (dry run)...")
        organize_files(dry_run=True)

What does this code do?

if __name__ == "__main__": ensures this block only runs when you execute the file directly (not when another script imports it). This will matter later when scheduler.py imports the organize_files function.

"--run" in sys.argv checks whether you typed --run after the script name when you ran it. If you did, the script moves files for real. If you did not, it defaults to the safe dry-run preview.

  • Save organizer.py.

ImportError when running the script later?

If you see an error about sys not being defined, double-check that import sys is at the top of your file with the other imports.

help me fix an import error in my organizer script

Test both modes

Time to see your organizer in action. You will run it twice: once to preview, once to execute.

  • Run the dry-run preview by running this command in your terminal:
python organizer.py

You should see a list of files with their proposed destinations, followed by the summary report and the hint to add --run.

What should I see?

Your output will look something like this (with your own file names):

Previewing changes (dry run)...
[DRY RUN] photo.jpg -> Images/
[DRY RUN] report.pdf -> Documents/
[DRY RUN] song.mp3 -> Audio/

--- Summary ---
  Audio: 1 files
  Documents: 1 files
  Images: 1 files
  Total: 3 files

This was a dry run. Add --run to actually move files.

If you see zero files, your Downloads folder may only contain subfolders (which the script skips). Drop a test file into Downloads and run again.

  • Now run the organizer for real by running:
python organizer.py --run
  • Open your Downloads folder in Finder (macOS) or File Explorer (Windows).
  • Confirm that new subfolders (Images, Documents, etc.) exist and contain the correct files.

Files didn't move?

Make sure you included the --run flag. Without it, the script only previews.

If you see a PermissionError, a file in your Downloads folder may be open in another program. Close it and try again.

help me troubleshoot why my organizer isn't moving files

✔️ Awesome, I've got everything!

Great. Double check you have saved organizer.py with all the changes.

ⓧ I'd like to double check the full code

Here is the complete organizer.py at this point:

from pathlib import Path
import shutil
import sys

# Configure your Downloads folder path
DOWNLOADS = Path.home() / "Downloads"

# File category rules - maps folder name to list of extensions
FILE_CATEGORIES = {
    "Images": [".jpg", ".jpeg", ".png", ".gif", ".svg", ".webp"],
    "Documents": [".pdf", ".doc", ".docx", ".txt", ".rtf", ".xlsx", ".pptx"],
    "Videos": [".mp4", ".avi", ".mkv", ".mov", ".wmv"],
    "Audio": [".mp3", ".wav", ".flac", ".aac", ".ogg"],
    "Archives": [".zip", ".rar", ".7z", ".tar", ".gz"],
    "Code": [".py", ".js", ".html", ".css", ".java", ".cpp"],
}


def get_category(file_path):
    """Determine which category a file belongs to based on its extension."""
    extension = file_path.suffix.lower()
    for category, extensions in FILE_CATEGORIES.items():
        if extension in extensions:
            return category
    return "Other"


def organize_files(dry_run=True):
    """Organize files in the Downloads folder by type."""
    if not DOWNLOADS.exists():
        print(f"Error: Downloads folder not found at {DOWNLOADS}")
        return

    counts = {}
    total = 0

    for file_path in DOWNLOADS.iterdir():
        if not file_path.is_file():
            continue

        category = get_category(file_path)
        destination_folder = DOWNLOADS / category
        destination = destination_folder / file_path.name

        # Update counts
        counts[category] = counts.get(category, 0) + 1
        total += 1

        if dry_run:
            print(f"[DRY RUN] {file_path.name} -> {category}/")
        else:
            destination_folder.mkdir(parents=True, exist_ok=True)
            shutil.move(str(file_path), str(destination))
            print(f"Moved: {file_path.name} -> {category}/")

    # Print summary report
    print("\n--- Summary ---")
    for category, count in sorted(counts.items()):
        print(f"  {category}: {count} files")
    print(f"  Total: {total} files")

    if dry_run and total > 0:
        print("\nThis was a dry run. Add --run to actually move files.")


if __name__ == "__main__":
    run_mode = "--run" in sys.argv

    if run_mode:
        print("Organizing files for real...")
        organize_files(dry_run=False)
    else:
        print("Previewing changes (dry run)...")
        organize_files(dry_run=True)

Your organizer now has a clean summary and a safe execution model. Next up, you will wire it into an automatic scheduler so your Downloads folder stays organized without any manual effort.

Schedule It to Run Automatically

Your organizer script now sorts files by type and gives you a clear summary of what happened. The only problem is you have to remember to run it manually.

In this step, you will wire up the schedule library to run your organizer automatically every 30 minutes. Once it is running, new files that land in your Downloads folder get sorted without you lifting a finger.

In this step, get ready to:
  • Write a wrapper function that calls your organizer on a timer.
  • Set up a 30-minute recurring schedule.
  • Test the scheduler with a real file and stop it cleanly.
Import schedule and write the wrapper function

The schedule library lets you define recurring jobs in plain Python. You will create a small wrapper function that calls your existing organizer and logs the time each run happens.

  • Switch to scheduler.py in VS Code and add the following code:
import schedule
import time
from organizer import organize_files


def run_organizer():
    """Run the file organizer and print status."""
    print(f"\n[{time.strftime('%H:%M:%S')}] Running organizer...")
    organize_files(dry_run=False)
    print("Done!")

What does this code do?

  • import schedule brings in the scheduling library you installed in Step 1.
  • from organizer import organize_files pulls your organizer function directly from organizer.py so the scheduler can call it.
  • run_organizer() is a thin wrapper that prints a timestamp before each run. This helps you confirm the scheduler is working when you check the terminal later.

Why a separate scheduler file?

Keeping the scheduling logic in its own file means you can still run organizer.py on-demand whenever you want a quick one-off sort. The scheduler is an independent process you start when you want ongoing automation.

Set up the recurring schedule and run loop

The schedule library uses a human-readable builder pattern. You tell it how often to run, point it at your function, then start a loop that checks for pending jobs.

  • In scheduler.py, add the following code below the run_organizer() function:
# Schedule the organizer to run every 30 minutes
schedule.every(30).minutes.do(run_organizer)

print("Downloads organizer scheduler started.")
print("Running every 30 minutes. Press Ctrl+C to stop.")
print(f"Next run at: {schedule.next_run()}")

while True:
    schedule.run_pending()
    time.sleep(60)

What does this code do?

  • schedule.every(30).minutes.do(run_organizer) registers your function to run every 30 minutes. The readable API makes the interval obvious at a glance.
  • The three print() statements confirm the scheduler started and tell you when the next run will happen.
  • while True keeps the script alive indefinitely. schedule.run_pending() checks whether any job is due and runs it. time.sleep(60) pauses for 60 seconds between checks to avoid burning CPU.
  • Save scheduler.py.

Seeing a syntax error when you save?

  • Make sure the f-string on the timestamp print line has matching curly braces and quotes.
  • Confirm the while loop is at the top level of the file (not indented inside a function).

Help me fix a syntax error in my scheduler.py file.

✔️ Awesome, I've got everything!

Great. Make sure you have saved the file before moving on.

ⓧ I'd like to double check the full code

import schedule
import time
from organizer import organize_files


def run_organizer():
    """Run the file organizer and print status."""
    print(f"\n[{time.strftime('%H:%M:%S')}] Running organizer...")
    organize_files(dry_run=False)
    print("Done!")


# Schedule the organizer to run every 30 minutes
schedule.every(30).minutes.do(run_organizer)

print("Downloads organizer scheduler started.")
print("Running every 30 minutes. Press Ctrl+C to stop.")
print(f"Next run at: {schedule.next_run()}")

while True:
    schedule.run_pending()
    time.sleep(60)
Run and test the scheduler

Time to see it in action. You will start the scheduler, drop a test file into Downloads, and confirm it gets moved automatically.

Speed up testing

Waiting 30 minutes is no fun. Temporarily change schedule.every(30).minutes to schedule.every(10).seconds in scheduler.py so you can see results quickly. Change it back to 30 minutes when you are done testing.

  • Start the scheduler by running this command in your terminal:
python scheduler.py

You should see three lines of output confirming the scheduler has started, the interval, and the next scheduled run time.

  • Open your Downloads folder in Finder (macOS) or File Explorer (Windows).
  • Drop a test file (any image, document, or text file) into your Downloads folder.
  • Wait for the next scheduled run (10 seconds if you used the testing interval, or 30 minutes if you kept the default).
  • Check your terminal. You should see the timestamp, your file being moved, and the summary report.
  • Confirm the test file has moved into the correct category subfolder in your Downloads folder.
  • Stop the scheduler by pressing Ctrl+C in your terminal.

What does Ctrl+C do?

Ctrl+C sends an interrupt signal to the running Python process. Since your scheduler uses an infinite while True loop, this is the standard way to stop it. The script exits immediately and your terminal goes back to normal.

Scheduler not picking up your test file?

  • If you are using the 30-minute interval, make sure you have waited long enough. Switch to the 10-second interval for faster feedback.
  • Make sure you saved scheduler.py after making changes. You need to stop the scheduler with Ctrl+C and restart it for code changes to take effect.
  • Check that your test file is sitting directly in the Downloads folder, not inside one of the category subfolders.

Help me debug why my scheduler isn't moving files.

  • If you changed the interval to 10 seconds for testing, switch it back to schedule.every(30).minutes.do(run_organizer) and save scheduler.py.

Secret mission

Add a Sort by Date Mode

Your organizer sorts files by type, but what about sorting by when files were created? Add a date-based mode that creates Year/Month folders like 2026/06-June, giving you a timeline view of your downloads.

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally, so there are no ongoing costs. Decide whether to keep your project files for future use, or delete them entirely.

Resources you used:

  • downloads-organizer project folder on your Desktop (contains organizer.py and scheduler.py).
  • schedule Python package installed via pip.
  • Organized subfolders created in your Downloads folder (Images, Documents, Videos, etc.).

Keep everything running

No action needed. Choose this if you plan to keep using the organizer to keep your Downloads folder tidy.

  • Your project files stay in the downloads-organizer folder on your Desktop.
  • The schedule package remains installed and ready to use.
  • Your organized subfolders in Downloads stay as they are.
  • You can run python scheduler.py any time to start the automatic organizer again.

Pause - I'll come back to this later

Stop any running processes but keep your files for later.

  • If scheduler.py is still running in a terminal, press Ctrl+C to stop it.
  • Your project files and the schedule package remain on your machine, ready for when you come back.

Delete - I don't want to use this again

Remove all project files and uninstall the schedule package.

  • If scheduler.py is still running, press Ctrl+C to stop it.
  • Uninstall the schedule package by running:
pip uninstall schedule
  • Delete the project folder by running:

macOS

rm -rf ~/Desktop/downloads-organizer

Windows

rmdir /s /q %USERPROFILE%\Desktop\downloads-organizer

What about the organized subfolders in Downloads?

The subfolders your script created (Images, Documents, Videos, etc.) contain your actual files. Do not delete these folders unless you first move the files back to your Downloads root.

If you want a flat Downloads folder again, open each subfolder and move the files back up one level manually.

That's a Wrap!

That's a Wrap!

Nice work! You just built a real automation tool that keeps your Downloads folder organized without any manual effort.

You've learned how to:

  • Build a file organizer that categorizes and moves files using Python's pathlib and shutil libraries.
  • Implement a dry-run safety pattern that previews changes before moving any files, preventing accidental data loss.
  • Wire up automatic scheduling with the schedule library so your folder stays clean without manual effort.
  • Secret Mission: Added a date-based organization mode using file metadata and datetime formatting to sort files into Year/Month folders.

Ready to quiz yourself?