├── .gitattributes ├── .github └── workflows │ ├── pythonlint_master.yml │ └── pythonlint.yml ├── LICENSE ├── README.md ├── .gitignore └── organize.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/workflows/pythonlint_master.yml: -------------------------------------------------------------------------------- 1 | name: PythonLint 2 | on: [pull_request] 3 | 4 | jobs: 5 | Flask8Test: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v1 9 | - name: Set up Python 3.7 10 | uses: actions/setup-python@v1 11 | with: 12 | python-version: 3.7 13 | 14 | - name: Install dependencies 15 | run: | 16 | python -m pip install --upgrade pip 17 | pip install flake8 18 | 19 | - name: Lint with flake8 20 | run: | 21 | # stop the build if there are Python syntax errors or undefined names 22 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 23 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 24 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 25 | -------------------------------------------------------------------------------- /.github/workflows/pythonlint.yml: -------------------------------------------------------------------------------- 1 | name: PythonLint 2 | on: 3 | push: 4 | branches: 5 | - master 6 | 7 | jobs: 8 | Flask8Test: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Set up Python 3.7 13 | uses: actions/setup-python@v1 14 | with: 15 | python-version: 3.7 16 | 17 | - name: Install dependencies 18 | run: | 19 | python -m pip install --upgrade pip 20 | pip install flake8 21 | 22 | - name: Lint with flake8 23 | run: | 24 | # stop the build if there are Python syntax errors or undefined names 25 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 26 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 27 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Jan B. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Downloads-organizer 2 | I made this project for the purpose of organizing my Donwloads-folder. 3 | The script creates several folders in Downloads and moves files with specific extensions to their corresponding folder. 4 | Folders that are downloaded are also automaticly moved to the 'FOLDERS' folder. 5 | The script is very easy to addapt to suit your needs, so feel free to tinquer with it. 6 | 7 | I recommend using .exe version of the script, which can easily be made with pyinstaller. 8 | I personnally put the .exe file in my startup folder so that it automaticly organizes my files when I log in on my computer (C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp). 9 | 10 | Based on suggestions, I made an exe version of the script which organizes the direcotry where the organize_local_directory.exe is located so that it can easily be used on any folder. 11 | 12 | This is my first project to be posted on github. So please adjust your expectations accordingly :) . 13 | 14 | ## Requirements 15 | The script only uses standard python modules so there is no need to install aditional modules. 16 | The script itself requires a Python interpreter to run. I normally run it in my IDE. 17 | 18 | ## How to use 19 | Change the directory_path variable in the organizer.py script and then run the script anyway you see fit. 20 | 21 | Alternatively you can run the organize_local_directory.exe to organize the folder where the exe file is located. 22 | 23 | ## Important 24 | Depending on the size of your (Downloads-) folder it can take quite a while for all the files to move/organize during the first run. Don't terminate the script while it's running because that may corrupt some of your files. 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/python,pycharm 3 | # Edit at https://www.gitignore.io/?templates=python,pycharm 4 | 5 | ### PyCharm ### 6 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 7 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 8 | 9 | # User-specific stuff 10 | .idea/**/workspace.xml 11 | .idea/**/tasks.xml 12 | .idea/**/usage.statistics.xml 13 | .idea/**/dictionaries 14 | .idea/**/shelf 15 | 16 | # Generated files 17 | .idea/**/contentModel.xml 18 | 19 | # Sensitive or high-churn files 20 | .idea/**/dataSources/ 21 | .idea/**/dataSources.ids 22 | .idea/**/dataSources.local.xml 23 | .idea/**/sqlDataSources.xml 24 | .idea/**/dynamic.xml 25 | .idea/**/uiDesigner.xml 26 | .idea/**/dbnavigator.xml 27 | 28 | # Gradle 29 | .idea/**/gradle.xml 30 | .idea/**/libraries 31 | 32 | # Gradle and Maven with auto-import 33 | # When using Gradle or Maven with auto-import, you should exclude module files, 34 | # since they will be recreated, and may cause churn. Uncomment if using 35 | # auto-import. 36 | # .idea/modules.xml 37 | # .idea/*.iml 38 | # .idea/modules 39 | # *.iml 40 | # *.ipr 41 | 42 | # CMake 43 | cmake-build-*/ 44 | 45 | # Mongo Explorer plugin 46 | .idea/**/mongoSettings.xml 47 | 48 | # File-based project format 49 | *.iws 50 | 51 | # IntelliJ 52 | out/ 53 | 54 | # mpeltonen/sbt-idea plugin 55 | .idea_modules/ 56 | 57 | # JIRA plugin 58 | atlassian-ide-plugin.xml 59 | 60 | # Cursive Clojure plugin 61 | .idea/replstate.xml 62 | 63 | # Crashlytics plugin (for Android Studio and IntelliJ) 64 | com_crashlytics_export_strings.xml 65 | crashlytics.properties 66 | crashlytics-build.properties 67 | fabric.properties 68 | 69 | # Editor-based Rest Client 70 | .idea/httpRequests 71 | 72 | # Android studio 3.1+ serialized cache file 73 | .idea/caches/build_file_checksums.ser 74 | 75 | ### PyCharm Patch ### 76 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 77 | 78 | # *.iml 79 | # modules.xml 80 | # .idea/misc.xml 81 | # *.ipr 82 | 83 | # Sonarlint plugin 84 | .idea/**/sonarlint/ 85 | 86 | # SonarQube Plugin 87 | .idea/**/sonarIssues.xml 88 | 89 | # Markdown Navigator plugin 90 | .idea/**/markdown-navigator.xml 91 | .idea/**/markdown-navigator/ 92 | 93 | ### Python ### 94 | # Byte-compiled / optimized / DLL files 95 | __pycache__/ 96 | *.py[cod] 97 | *$py.class 98 | 99 | # C extensions 100 | *.so 101 | 102 | # Distribution / packaging 103 | .Python 104 | build/ 105 | develop-eggs/ 106 | dist/ 107 | downloads/ 108 | eggs/ 109 | .eggs/ 110 | lib/ 111 | lib64/ 112 | parts/ 113 | sdist/ 114 | var/ 115 | wheels/ 116 | pip-wheel-metadata/ 117 | share/python-wheels/ 118 | *.egg-info/ 119 | .installed.cfg 120 | *.egg 121 | MANIFEST 122 | 123 | # PyInstaller 124 | # Usually these files are written by a python script from a template 125 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 126 | *.manifest 127 | *.spec 128 | 129 | # Installer logs 130 | pip-log.txt 131 | pip-delete-this-directory.txt 132 | 133 | # Unit test / coverage reports 134 | htmlcov/ 135 | .tox/ 136 | .nox/ 137 | .coverage 138 | .coverage.* 139 | .cache 140 | nosetests.xml 141 | coverage.xml 142 | *.cover 143 | .hypothesis/ 144 | .pytest_cache/ 145 | 146 | # Translations 147 | *.mo 148 | *.pot 149 | 150 | # Scrapy stuff: 151 | .scrapy 152 | 153 | # Sphinx documentation 154 | docs/_build/ 155 | 156 | # PyBuilder 157 | target/ 158 | 159 | # pyenv 160 | .python-version 161 | 162 | # pipenv 163 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 164 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 165 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 166 | # install all needed dependencies. 167 | #Pipfile.lock 168 | 169 | # celery beat schedule file 170 | celerybeat-schedule 171 | 172 | # SageMath parsed files 173 | *.sage.py 174 | 175 | # Spyder project settings 176 | .spyderproject 177 | .spyproject 178 | 179 | # Rope project settings 180 | .ropeproject 181 | 182 | # Mr Developer 183 | .mr.developer.cfg 184 | .project 185 | .pydevproject 186 | 187 | # mkdocs documentation 188 | /site 189 | 190 | # mypy 191 | .mypy_cache/ 192 | .dmypy.json 193 | dmypy.json 194 | 195 | # Pyre type checker 196 | .pyre/ 197 | 198 | # End of https://www.gitignore.io/api/python,pycharm 199 | -------------------------------------------------------------------------------- /organize.py: -------------------------------------------------------------------------------- 1 | """ 2 | Titel: Downloads organizer 3 | Author: Jan B. 4 | Date: april 2020 5 | Version: 1.0 6 | Python-version: 3.7 7 | 8 | This program is designed to organize the contents of a folder by assigning 9 | files with specific extensions to specific folders. 10 | This program is made with the purpose of organzing the windows download-folder, 11 | but by changing the directory_path-variable in the main it can tho(probably) be 12 | used for any folder. 13 | The directories-dictionary in the main specifies the names of the folders as 14 | well as the file extensions that should be assigned to those folders. 15 | You can change this dictionary to suit your needs. 16 | 17 | 18 | MIT License 19 | 20 | Copyright (c) [2020] [Jan B.] 21 | 22 | Permission is hereby granted, free of charge, to any person obtaining a copy 23 | of this software and associated documentation files (the "Software"), to deal 24 | in the Software without restriction, including without limitation the rights 25 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 26 | copies of the Software, and to permit persons to whom the Software is 27 | furnished to do so, subject to the following conditions: 28 | 29 | The above copyright notice and this permission notice shall be included in all 30 | copies or substantial portions of the Software. 31 | 32 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 33 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 34 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 35 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 36 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 37 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 38 | SOFTWARE. 39 | """ 40 | 41 | # imports: 42 | import os 43 | import shutil 44 | import sys 45 | 46 | 47 | def create_folders(directories, directory_path): 48 | """ 49 | This function creates the folders in where the files 50 | will be moved to. 51 | :param directories: dictionary, this is a dictionary containing the 52 | names of the sorted folders and the extensions that correspond to those 53 | folders. 54 | :param directory_path: string, this is a string of the path to the 55 | directory that is to be sorted. 56 | """ 57 | for key in directories: 58 | if key not in os.listdir(directory_path): 59 | os.mkdir(os.path.join(directory_path, key)) 60 | if "OTHER" not in os.listdir(directory_path): 61 | os.mkdir(os.path.join(directory_path, "OTHER")) 62 | 63 | 64 | def organize_folders(directories, directory_path): 65 | """ 66 | This function organizes the files in the specified folder into folders 67 | :param directories: directories: dictionary, this is a dictionary 68 | containing the names of the sorted folders and the extensions that 69 | correspond to those folders. 70 | :param directory_path: string, this is a string of the path to the 71 | directory that is to be sorted. 72 | """ 73 | for file in os.listdir(directory_path): 74 | if os.path.isfile(os.path.join(directory_path, file)): 75 | src_path = os.path.join(directory_path, file) 76 | for key in directories: 77 | extension = directories[key] 78 | if file.endswith(extension): 79 | dest_path = os.path.join(directory_path, key, file) 80 | shutil.move(src_path, dest_path) 81 | break 82 | 83 | 84 | def organize_remaining_files(directory_path): 85 | """ 86 | This function assigns the file that don't have a corresponding folder to 87 | the directory. 88 | :param directory_path: string, this is a string of the path to the 89 | directory that is to be sorted. 90 | """ 91 | for file in os.listdir(directory_path): 92 | if os.path.isfile(os.path.join(directory_path, file)): 93 | src_path = os.path.join(directory_path, file) 94 | dest_path = os.path.join(directory_path, "OTHER", file) 95 | shutil.move(src_path, dest_path) 96 | 97 | 98 | def organize_remaining_folders(directories, directory_path): 99 | """ 100 | This function assings the folders within the specified directory to the 101 | directory. 102 | :param directories: directories: dictionary, this is a dictionary 103 | containing the names of the sorted folders and the extensions that 104 | corresponds to those folders. 105 | :param directory_path: string, this is a string of the path to the 106 | directory that is to be sorted. 107 | """ 108 | list_dir = os.listdir(directory_path) 109 | organized_folders = [] 110 | for folder in directories: 111 | organized_folders.append(folder) 112 | organized_folders = tuple(organized_folders) 113 | for folder in list_dir: 114 | if folder not in organized_folders: 115 | src_path = os.path.join(directory_path, folder) 116 | dest_path = os.path.join(directory_path, "FOLDERS", folder) 117 | try: 118 | shutil.move(src_path, dest_path) 119 | except shutil.Error: 120 | shutil.move(src_path, dest_path + " - copy") 121 | print("That folder already exists in the destination folder." 122 | "\nThe folder is renamed to '{}'".format(folder + " - copy")) 123 | 124 | 125 | if __name__ == '__main__': 126 | directory_path = "C:/Users/jan_b/Downloads" 127 | directories = { 128 | "HTML": (".html5", ".html", ".htm", ".xhtml"), 129 | "IMAGES": (".jpeg", ".jpg", ".tiff", ".gif", ".bmp", ".png", ".bpg", 130 | "svg", 131 | ".heif", ".psd"), 132 | "VIDEOS": (".avi", ".flv", ".wmv", ".mov", ".mp4", ".webm", ".vob", 133 | ".mng", 134 | ".qt", ".mpg", ".mpeg", ".3gp", ".mkv"), 135 | "DOCUMENTS": (".oxps", ".epub", ".pages", ".docx", ".doc", ".fdf", 136 | ".ods", 137 | ".odt", ".pwi", ".xsn", ".xps", ".dotx", ".docm", ".dox", 138 | ".rvg", ".rtf", ".rtfd", ".wpd", ".xls", ".xlsx", ".ppt", 139 | "pptx"), 140 | "ARCHIVES": (".a", ".ar", ".cpio", ".iso", ".tar", ".gz", ".rz", ".7z", 141 | ".dmg", ".rar", ".xar", ".zip"), 142 | "AUDIO": (".aac", ".aa", ".aac", ".dvf", ".m4a", ".m4b", ".m4p", 143 | ".mp3", 144 | ".msv", "ogg", "oga", ".raw", ".vox", ".wav", ".wma"), 145 | "PLAINTEXT": (".txt", ".in", ".out"), 146 | "PDF": ".pdf", 147 | "PYTHON": ".py", 148 | "EXE": ".exe", 149 | "OTHER": "", 150 | "FOLDERS": "" 151 | } 152 | try: 153 | create_folders(directories, directory_path) 154 | organize_folders(directories, directory_path) 155 | organize_remaining_files(directory_path) 156 | organize_remaining_folders(directories, directory_path) 157 | except shutil.Error: 158 | print("There was an error trying to move an item to its destination folder") 159 | 160 | --------------------------------------------------------------------------------