Skip to content

📥 My Downloads Folder is a Mess

✏️ Section in progress

This section is incomplete. Please come back soon.

🎯 The Problem

It was clean two weeks ago, I swear. But yet again, my downloads folder has become a mountainous dump of assorted images, videos, documents, audio files, app installers, spreadsheets, and what might possibly be a virus or two?? My friend has a chronic hoarding problem and even they held an intervention for me 😖.

My Downloads Folder

Fig 1. My downloads folder. Maybe the intervention was warranted ...

Photo by Collab Media on Unsplash

It's gotten so bad that everytime I muster up the motivation to clean it, the sheer number and diversity of content in the folder makes me question why I had the gall to even try. I just really dread having to manually prune away uneeded files and sort away the rest into the right spots elsewhere in my computer. It's a benign enough problem that I can procrastinate it without consequence, but it just gets worse with every new download. (Yeah, yeah I know I should put things in the right spot when I first download it, but I haven't yet unlocked 'discpline' and 'organization' in my skill tree 🤷‍♀️).

It would be pretty nice if I could at least group the files into bins like "Office Work", "Audio", "Video", "Images", and "Apps", etc. But even forcing myself to spend time doing that step is pretty daunting, especially when I have so many other things to do.

Of course, the biggest issue is I know that even if I get super motivated and power through it, the folder will become a landfill of files in two weeks. The last intervention was super cringe, I don't think I could handle another one 😭.

If only there was an easy, repeatable, and automatic way to organize my downloads folder ...

🤚 Before We Begin

As always before a new project, create a new folder on your computer, then open the folder in VS Code.

🐍 The Code

As a general rule, it's a good idea to develop your code in a low stakes setting. Especially since we will be working with files, it's unfortunately easy to command your computer to do something you didn't intend because we can mistakes when writing code and our computer is not smart enough to not follow our mistaken instructions.

So, we should create some test files. We could do this by hand, but why not automate this first?

downloadsfolder.py
"""downloads_0

Clean up on aisle 'Downloads'
"""

from pathlib import Path

# Make fake downloads folder
downloads_folder = Path("./downloads")
downloads_folder.mkdir(parents=True, exist_ok=True)

After running the code, you should see a new folder named downloads

downloadsfolder.py
"""downloads_1

Clean up on aisle 'Downloads'
"""

from pathlib import Path

# Make fake downloads folder
downloads_folder = Path("./downloads")
downloads_folder.mkdir(parents=True, exist_ok=True)

# List files
for file in downloads_folder.iterdir():
    print(file)
downloadsfolder.py
"""downloads_2

Clean up on aisle 'Downloads'
"""

import random
from pathlib import Path

# Make fake downloads folder
downloads_folder = Path("./downloads")
downloads_folder.mkdir(parents=True, exist_ok=True)

# Add fake files
filetypes = ["mp4", "docx", "txt", "wav", "xlsx", "pdf"]
num_files = 10
for i in range(num_files):
    file = downloads_folder/f"fake_{i}.{random.choice(filetypes)}"
    file.touch()

# List files
for file in downloads_folder.iterdir():
    print(file)
downloadsfolder.py
"""downloads_3

Clean up on aisle 'Downloads'
"""

import random
from pathlib import Path

# Make fake downloads folder
downloads_folder = Path("./downloads")
downloads_folder.mkdir(parents=True, exist_ok=True)

# Add fake files
filetypes = ["mp4", "docx", "txt", "wav", "xlsx", "pdf"]
num_files = 10
for i in range(num_files):
    file = downloads_folder/f"fake_{i}.{random.choice(filetypes)}"
    file.touch()

# Sort files
for file in downloads_folder.iterdir():

    # Assign the file to a group
    group = ""
    match file.suffix:
        case ".txt"|".pdf"|".doc"|".docx"|".xls"|".xlsx"|".ppt"|".pptx":
            group = "docs"
        case ".mov"|".mp4"|".avi":
            group = "videos"
        case ".png"|".jpeg"|".jpg"|".gif"|".bmp"|".webp":
            group = "images"
        case ".wav"|".mp3"|".m4a"|".aac"|".flac":
            group = "audio"
        case ".exe"|".msi"|".dmg"|".app":
            group = "apps"

    # Move the file to the new group
    destination = Path(f"{file.parent}/{group}")
    destination.mkdir(parents=True, exist_ok=True)
    file.move(destination/file.name)

Improving Code Organization

We have some duplication where we define a bunch of filetypes for our random file generator, and again we have lists of filetypes in our match statement.

We can actually remove this duplication and simultaneously improve the flow of our code if we think a bit more carefully about how we choose to organize our data.

A dictionary is a data structure which maps keys to values. This was discussed in the previous chapter, so go back and re-read if you need a refresher.

We can use a dictionary to define the mapping of categories to filetypes. Something like this:

{
    ".pdf": "docs",
    ".docx": "docs",
    ".wav": "audio",
    # ...etc
}

However, even this has quite a bit of duplication and is a bit annoying to type. Instead, we can reverse the mapping:

{
    "docs": {".pdf", ".docx"},
    "audio": {".wav"}
}

which hopefully seems a bit more intuitive to write down anyways. Then we dynamically generate the "filetype -> category" table using some extra code.

downloadsfolder.py
"""downloads_4

Clean up on aisle 'Downloads'
"""

import random
from pathlib import Path

downloads_folder = Path("./downloads4")
downloads_folder.mkdir(parents=True, exist_ok=True)

# Define categories
categories = {
    "docs": {".txt", ".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx"},
    "audio": {".wav", ".mp3", ".aac", ".flac", ".m4a"},
    "video": {".mp4", ".mkv", ".mov", ".avi", ".wmv"},
    "images": {".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp"},
    "archives": {".zip", ".rar", ".7z", ".tar", ".gz"},
    "apps": {".exe", ".msi", ".app", ".dmg"},
}

# Create mapping from filetype to category
extension_to_category = {}
for category, extensions in categories.items():
    for e in extensions:
        extension_to_category[e] = category

# Randomly generate test files
num_files = 100
filetypes = list(extension_to_category.keys())
for i in range(num_files):
    file = downloads_folder/f"fake_{i}{random.choice(filetypes)}"
    file.touch()

# Organize
for file in downloads_folder.iterdir():
    group = extension_to_category.get(file.suffix, None)
    if group is None:
        continue
    destination = Path(f"{file.parent}/{group}")
    destination.mkdir(parents=True, exist_ok=True)
    file.move(destination/file.name)

Et voila, I no longer need the intervention for my downloads folder.

🪞 Reflection