41

I already found this question that suggests to use os.path.expanduser(path) to get the user's home directory.

I would like to achieve the same with the "Downloads" folder. I know that this is possible in C#, yet I'm new to Python and don't know if this is possible here too, preferable platform-independent (Windows, Ubuntu).

I know that I just could do download_folder = os.path.expanduser("~")+"/Downloads/", yet (at least in Windows) it is possible to change the Default download folder.

Community
  • 1
  • 1
Markus Weninger
  • 11,931
  • 7
  • 64
  • 137
  • 2
    With sufficient ctypes-foo you could adapt [the Windows-specific code in this answer](http://stackoverflow.com/a/7672816/1600898) to Python (with a fallback to `os.expanduser(...)` on non-Windows platforms). [Here](http://stackoverflow.com/a/29888752/1600898) is an example. Note, however, that a "Downloads" directory is not a platform-independent concept. It is quite possible to encounter Linux systems that don't have one, so be sure to create it if it doesn't exist. – user4815162342 Mar 07 '16 at 18:41
  • @user4815162342: Okay, thanks for the advice with `a "Downloads" directory is not a platform-independent concept.`! – Markus Weninger Mar 07 '16 at 18:43

7 Answers7

46
from pathlib import Path
downloads_path = str(Path.home() / "Downloads")
Shmidt
  • 16,436
  • 18
  • 88
  • 136
  • 5
    I am pretty sure this does not account for relocation on Windows as per the OP. In fact, I am pretty sure that Windows does not actually require the Downloads directory to be located within anything that looks like a "home" directory. – Karl Knechtel Jan 29 '22 at 04:36
  • Yes not only can the Windows Downloads directory be renamed or moved, on non-English systems it is localized into another language even in a fresh, default setup. – hippietrail Apr 10 '23 at 04:23
23

This fairly simple solution (expanded from this reddit post) worked for me

import os

def get_download_path():
    """Returns the default downloads path for linux or windows"""
    if os.name == 'nt':
        import winreg
        sub_key = r'SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders'
        downloads_guid = '{374DE290-123F-4565-9164-39C4925E467B}'
        with winreg.OpenKey(winreg.HKEY_CURRENT_USER, sub_key) as key:
            location = winreg.QueryValueEx(key, downloads_guid)[0]
        return location
    else:
        return os.path.join(os.path.expanduser('~'), 'downloads')
  • The GUID can be obtained from Microsoft's KNOWNFOLDERID docs
  • This can be expanded to work more generically other directories
Alexander McFarlane
  • 10,643
  • 9
  • 59
  • 100
  • 1
    But this would find the downloads folder path of a local server. How do you find the downloads folder of the client if the same is to be acheived for a web app? – user10058776 Jun 08 '21 at 20:54
  • 2
    you cannot do this server side for obvious reasons - you will need to embed some kind of script in your web app that is ran by the end user's browser – Alexander McFarlane Jun 11 '21 at 15:25
  • Most linux distributions are case sensitive, and most linux distributions name the folder with a capital 'D'. Therefore, adding a lower case 'downloads' to the user path doesn't work. You should use 'Downloads' instead. – PoDuck Sep 06 '22 at 15:45
13

Correctly locating Windows folders is somewhat of a chore in Python. According to answers covering Microsoft development technologies, such as this one, they should be obtained using the Vista Known Folder API. This API is not wrapped by the Python standard library (though there is an issue from 2008 requesting it), but one can use the ctypes module to access it anyway.

Adapting the above answer to use the folder id for downloads shown here and combining it with your existing Unix code should result in code that looks like this:

import os

if os.name == 'nt':
    import ctypes
    from ctypes import windll, wintypes
    from uuid import UUID

    # ctypes GUID copied from MSDN sample code
    class GUID(ctypes.Structure):
        _fields_ = [
            ("Data1", wintypes.DWORD),
            ("Data2", wintypes.WORD),
            ("Data3", wintypes.WORD),
            ("Data4", wintypes.BYTE * 8)
        ] 

        def __init__(self, uuidstr):
            uuid = UUID(uuidstr)
            ctypes.Structure.__init__(self)
            self.Data1, self.Data2, self.Data3, \
                self.Data4[0], self.Data4[1], rest = uuid.fields
            for i in range(2, 8):
                self.Data4[i] = rest>>(8-i-1)*8 & 0xff

    SHGetKnownFolderPath = windll.shell32.SHGetKnownFolderPath
    SHGetKnownFolderPath.argtypes = [
        ctypes.POINTER(GUID), wintypes.DWORD,
        wintypes.HANDLE, ctypes.POINTER(ctypes.c_wchar_p)
    ]

    def _get_known_folder_path(uuidstr):
        pathptr = ctypes.c_wchar_p()
        guid = GUID(uuidstr)
        if SHGetKnownFolderPath(ctypes.byref(guid), 0, 0, ctypes.byref(pathptr)):
            raise ctypes.WinError()
        return pathptr.value

    FOLDERID_Download = '{374DE290-123F-4565-9164-39C4925E467B}'

    def get_download_folder():
        return _get_known_folder_path(FOLDERID_Download)
else:
    def get_download_folder():
        home = os.path.expanduser("~")
        return os.path.join(home, "Downloads")

A more complete module for retrieving known folders from Python is available on github.

Community
  • 1
  • 1
user4815162342
  • 141,790
  • 18
  • 296
  • 355
  • Thanks for the detailed investigation and the link to the issue. – Markus Weninger Mar 07 '16 at 19:26
  • 1
    @MarkusWeninger You might also want to take a look at [this more complete wrapper](https://gist.github.com/mkropat/7550097) for `SHGetKnownFolderPath`. – user4815162342 Mar 08 '16 at 16:05
  • Thanks! I will look into it. `get_download_folder()` currently crashes with the following error. `File ".\downloadfolders.py", line 34, in _get_known_folder_path if SHGetKnownFolderPath(ctypes.byref(guid), 0, 0, ctypes.byref(pathptr)): ctypes.ArgumentError: argument 4: : expected LP_c_char_p instance instead of pointer to c_wchar_p` – Markus Weninger Mar 08 '16 at 16:07
  • Yes, `print(get_download_folder())` now prints my download folder (using Windows 10). Thanks a lot again, a pitty I cannot upvote again ;) – Markus Weninger Mar 08 '16 at 16:39
  • @MarkusWeninger You're welcome. Answer amended to remove the "untested" claim. :) – user4815162342 Mar 09 '16 at 07:22
  • The issue is now closed solved claiming that the appdirs module solves the problem. Appdirs has since been superseded by the platformdirs module. The modules address some directory concepts but not the downloads directory. I can't find another Python module that does. – hippietrail Apr 10 '23 at 04:58
13

For python3+ mac or linux

from pathlib import Path
path_to_download_folder = str(os.path.join(Path.home(), "Downloads"))
Jeff WR
  • 178
  • 1
  • 3
  • 1
    The downloads folder is localized on Windows so non-English systems call it something else. Furthermore the user can rename it or move it anyway. – hippietrail Apr 10 '23 at 04:44
2

Some linux distributions localize the name of the Downloads folder. E.g. after changing my locale to zh_TW, the Downloads folder became /home/user/下載. The correct way on linux distributions (using xdg-utils from freedesktop.org) is to call xdg-user-dir:

import subprocess
# Copy windows part from other answers here
try:
    folder = subprocess.run(["xdg-user-dir", "DOWNLOAD"],
                            capture_output=True, text=True).stdout.strip("\n")
except FileNotFoundError:  # if the command is missing
    import os.path
    folder = os.path.expanduser("~/Downloads")  # fallback

Note that the use of capture_output requires Python ≥3.7. If you already use GLib or don't mind adding more dependencies, see also these approaches using packages.

Dodezv
  • 266
  • 2
  • 8
0

For python3 on windows try:

import os

folder = os.path.join(os.path.join(os.environ['USERPROFILE']), 'folder_name')
print(folder)
0

I tried this so it can check the downloads folder even if device's language is not english:

from pathlib import Path
from googletrans import Translator


translator = Translator()
downloads = "Downloads"
downloads_path = str(Path.home() / downloads)

if str(downloads_path.split("\\")[-1]) != downloads:
    en_path = translator.translate(str(downloads_path.split("\\")[-1]), dest="en")
    if en_path.text == "downloads" or en_path.text == "Downloads":
        downloads_path = str(Path.home() / str(en_path.text))
Ayoub
  • 1
  • 1
  • 3