downsweep 0.1.0__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- downsweep-0.2.0/LICENSE +21 -0
- downsweep-0.2.0/PKG-INFO +169 -0
- downsweep-0.2.0/README.md +150 -0
- downsweep-0.2.0/downsweep/cli.py +143 -0
- downsweep-0.2.0/downsweep.egg-info/PKG-INFO +169 -0
- {downsweep-0.1.0 → downsweep-0.2.0}/downsweep.egg-info/SOURCES.txt +1 -0
- downsweep-0.2.0/pyproject.toml +31 -0
- downsweep-0.1.0/PKG-INFO +0 -36
- downsweep-0.1.0/README.md +0 -27
- downsweep-0.1.0/downsweep/cli.py +0 -83
- downsweep-0.1.0/downsweep.egg-info/PKG-INFO +0 -36
- downsweep-0.1.0/pyproject.toml +0 -18
- {downsweep-0.1.0 → downsweep-0.2.0}/downsweep/__init__.py +0 -0
- {downsweep-0.1.0 → downsweep-0.2.0}/downsweep.egg-info/dependency_links.txt +0 -0
- {downsweep-0.1.0 → downsweep-0.2.0}/downsweep.egg-info/entry_points.txt +0 -0
- {downsweep-0.1.0 → downsweep-0.2.0}/downsweep.egg-info/top_level.txt +0 -0
- {downsweep-0.1.0 → downsweep-0.2.0}/setup.cfg +0 -0
downsweep-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hari Prasath S
|
|
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.
|
downsweep-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: downsweep
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Sort a messy Downloads folder into neat folders with one command. Includes preview and undo.
|
|
5
|
+
Author: Hari Prasath S
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/stonefrozen096-collab/downsweep
|
|
8
|
+
Project-URL: Source, https://github.com/stonefrozen096-collab/downsweep
|
|
9
|
+
Project-URL: Issues, https://github.com/stonefrozen096-collab/downsweep/issues
|
|
10
|
+
Keywords: downloads,organizer,cleaner,files,cli,undo
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Utilities
|
|
15
|
+
Requires-Python: >=3.8
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
\# downsweep
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
\*\*Sort a messy Downloads folder into neat folders with one command. Changed your mind? Undo it.\*\*
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
!\[PyPI](https://img.shields.io/pypi/v/downsweep) !\[License](https://img.shields.io/pypi/l/downsweep) !\[Python](https://img.shields.io/pypi/pyversions/downsweep)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
Your Downloads folder is full of PDFs, installers, screenshots and zip files
|
|
33
|
+
|
|
34
|
+
all mixed together. `downsweep` sorts them into Images, PDFs, Documents,
|
|
35
|
+
|
|
36
|
+
Programs and more in a few seconds, and lets you reverse it if you don't like
|
|
37
|
+
|
|
38
|
+
the result.
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
\## Install
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
pip install downsweep
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
\## Quick start
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
See what would happen first. Nothing is moved:
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
downsweep --preview
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
Clean your Downloads folder:
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
downsweep
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
Changed your mind? Move everything back:
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
downsweep --undo
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
Clean any other folder:
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
downsweep "C:\\path\\to\\folder"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
\## What you will see
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
Moved: report.pdf -> PDFs
|
|
91
|
+
|
|
92
|
+
Moved: photo.jpg -> Images
|
|
93
|
+
|
|
94
|
+
Moved: setup.exe -> Programs
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
Summary
|
|
99
|
+
|
|
100
|
+
-------
|
|
101
|
+
|
|
102
|
+
PDFs 40
|
|
103
|
+
|
|
104
|
+
Programs 30
|
|
105
|
+
|
|
106
|
+
Images 12
|
|
107
|
+
|
|
108
|
+
Total 82
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
Done! Changed your mind? Run: downsweep --undo
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
\## Features
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
| Feature | What it does |
|
|
121
|
+
|
|
122
|
+
| --- | --- |
|
|
123
|
+
|
|
124
|
+
| Preview mode | Shows what would move, changes nothing |
|
|
125
|
+
|
|
126
|
+
| Undo | Moves the last cleanup's files back where they were |
|
|
127
|
+
|
|
128
|
+
| Summary | Prints how many files went into each folder |
|
|
129
|
+
|
|
130
|
+
| Safe by design | Never overwrites a file, adds (1), (2) to duplicate names |
|
|
131
|
+
|
|
132
|
+
| Download-aware | Skips files still downloading, like .crdownload and .part |
|
|
133
|
+
|
|
134
|
+
| Zero dependencies | Only uses Python's standard library |
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
\## Folders it creates
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
Images, Videos, Audio, PDFs, Documents, Archives, Programs, Code and Other.
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
\## Good to know
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
\- Undo reverses the \*\*last\*\* cleanup of a folder only.
|
|
151
|
+
|
|
152
|
+
\- After a cleanup, a small file called `.downsweep\_log.json` is saved inside the
|
|
153
|
+
|
|
154
|
+
folder. That is how undo remembers what moved. Please don't delete it if you
|
|
155
|
+
|
|
156
|
+
want to undo.
|
|
157
|
+
|
|
158
|
+
\- Only files directly inside the folder are sorted. Existing sub-folders are
|
|
159
|
+
|
|
160
|
+
left alone.
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
\## License
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
MIT
|
|
169
|
+
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
\# downsweep
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
\*\*Sort a messy Downloads folder into neat folders with one command. Changed your mind? Undo it.\*\*
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
!\[PyPI](https://img.shields.io/pypi/v/downsweep) !\[License](https://img.shields.io/pypi/l/downsweep) !\[Python](https://img.shields.io/pypi/pyversions/downsweep)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
Your Downloads folder is full of PDFs, installers, screenshots and zip files
|
|
14
|
+
|
|
15
|
+
all mixed together. `downsweep` sorts them into Images, PDFs, Documents,
|
|
16
|
+
|
|
17
|
+
Programs and more in a few seconds, and lets you reverse it if you don't like
|
|
18
|
+
|
|
19
|
+
the result.
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
\## Install
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
pip install downsweep
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
\## Quick start
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
See what would happen first. Nothing is moved:
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
downsweep --preview
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
Clean your Downloads folder:
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
downsweep
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
Changed your mind? Move everything back:
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
downsweep --undo
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
Clean any other folder:
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
downsweep "C:\\path\\to\\folder"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
\## What you will see
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
Moved: report.pdf -> PDFs
|
|
72
|
+
|
|
73
|
+
Moved: photo.jpg -> Images
|
|
74
|
+
|
|
75
|
+
Moved: setup.exe -> Programs
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
Summary
|
|
80
|
+
|
|
81
|
+
-------
|
|
82
|
+
|
|
83
|
+
PDFs 40
|
|
84
|
+
|
|
85
|
+
Programs 30
|
|
86
|
+
|
|
87
|
+
Images 12
|
|
88
|
+
|
|
89
|
+
Total 82
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
Done! Changed your mind? Run: downsweep --undo
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
\## Features
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
| Feature | What it does |
|
|
102
|
+
|
|
103
|
+
| --- | --- |
|
|
104
|
+
|
|
105
|
+
| Preview mode | Shows what would move, changes nothing |
|
|
106
|
+
|
|
107
|
+
| Undo | Moves the last cleanup's files back where they were |
|
|
108
|
+
|
|
109
|
+
| Summary | Prints how many files went into each folder |
|
|
110
|
+
|
|
111
|
+
| Safe by design | Never overwrites a file, adds (1), (2) to duplicate names |
|
|
112
|
+
|
|
113
|
+
| Download-aware | Skips files still downloading, like .crdownload and .part |
|
|
114
|
+
|
|
115
|
+
| Zero dependencies | Only uses Python's standard library |
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
\## Folders it creates
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
Images, Videos, Audio, PDFs, Documents, Archives, Programs, Code and Other.
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
\## Good to know
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
\- Undo reverses the \*\*last\*\* cleanup of a folder only.
|
|
132
|
+
|
|
133
|
+
\- After a cleanup, a small file called `.downsweep\_log.json` is saved inside the
|
|
134
|
+
|
|
135
|
+
folder. That is how undo remembers what moved. Please don't delete it if you
|
|
136
|
+
|
|
137
|
+
want to undo.
|
|
138
|
+
|
|
139
|
+
\- Only files directly inside the folder are sorted. Existing sub-folders are
|
|
140
|
+
|
|
141
|
+
left alone.
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
\## License
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
MIT
|
|
150
|
+
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import json
|
|
3
|
+
import shutil
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
CATEGORIES = {
|
|
7
|
+
"Images": [".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".svg", ".avif", ".jfif"],
|
|
8
|
+
"Videos": [".mp4", ".mkv", ".avi", ".mov", ".webm"],
|
|
9
|
+
"Audio": [".mp3", ".wav", ".flac", ".m4a"],
|
|
10
|
+
"PDFs": [".pdf"],
|
|
11
|
+
"Documents": [".doc", ".docx", ".txt", ".ppt", ".pptx", ".xls", ".xlsx", ".csv"],
|
|
12
|
+
"Archives": [".zip", ".rar", ".7z", ".tar", ".gz"],
|
|
13
|
+
"Programs": [".exe", ".msi"],
|
|
14
|
+
"Code": [".py", ".js", ".html", ".htm", ".css", ".java", ".cpp", ".ipynb"],
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
# Files still being downloaded, or system files - never touch these
|
|
18
|
+
SKIP = {".crdownload", ".part", ".tmp", ".ini", ".lnk"}
|
|
19
|
+
|
|
20
|
+
# Hidden file where the last cleanup is remembered, so it can be undone
|
|
21
|
+
LOG_NAME = ".downsweep_log.json"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def get_category(file):
|
|
25
|
+
ext = file.suffix.lower()
|
|
26
|
+
for name, extensions in CATEGORIES.items():
|
|
27
|
+
if ext in extensions:
|
|
28
|
+
return name
|
|
29
|
+
return "Other"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def unique_path(path):
|
|
33
|
+
# If a file with the same name exists, add (1), (2)... instead of overwriting
|
|
34
|
+
if not path.exists():
|
|
35
|
+
return path
|
|
36
|
+
counter = 1
|
|
37
|
+
while True:
|
|
38
|
+
new_path = path.with_name(f"{path.stem} ({counter}){path.suffix}")
|
|
39
|
+
if not new_path.exists():
|
|
40
|
+
return new_path
|
|
41
|
+
counter += 1
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def print_summary(counts):
|
|
45
|
+
print("\nSummary")
|
|
46
|
+
print("-------")
|
|
47
|
+
for name, number in sorted(counts.items(), key=lambda pair: -pair[1]):
|
|
48
|
+
print(f"{name:<12} {number}")
|
|
49
|
+
print(f"{'Total':<12} {sum(counts.values())}")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def clean(folder, preview):
|
|
53
|
+
moves = []
|
|
54
|
+
counts = {}
|
|
55
|
+
for item in sorted(folder.iterdir()):
|
|
56
|
+
if not item.is_file() or item.suffix.lower() in SKIP or item.name == LOG_NAME:
|
|
57
|
+
continue
|
|
58
|
+
category = get_category(item)
|
|
59
|
+
counts[category] = counts.get(category, 0) + 1
|
|
60
|
+
if preview:
|
|
61
|
+
print(f"Would move: {item.name} -> {category}")
|
|
62
|
+
else:
|
|
63
|
+
target_dir = folder / category
|
|
64
|
+
target_dir.mkdir(exist_ok=True)
|
|
65
|
+
target = unique_path(target_dir / item.name)
|
|
66
|
+
shutil.move(str(item), str(target))
|
|
67
|
+
moves.append({"from": str(item), "to": str(target)})
|
|
68
|
+
print(f"Moved: {item.name} -> {category}")
|
|
69
|
+
|
|
70
|
+
if not counts:
|
|
71
|
+
print("Nothing to sort here.")
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
print_summary(counts)
|
|
75
|
+
if preview:
|
|
76
|
+
print("\nNothing was changed.")
|
|
77
|
+
else:
|
|
78
|
+
(folder / LOG_NAME).write_text(json.dumps(moves, indent=2), encoding="utf-8")
|
|
79
|
+
print("\nDone! Changed your mind? Run: downsweep --undo")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def undo(folder):
|
|
83
|
+
log_file = folder / LOG_NAME
|
|
84
|
+
if not log_file.exists():
|
|
85
|
+
print("Nothing to undo.")
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
moves = json.loads(log_file.read_text(encoding="utf-8"))
|
|
89
|
+
restored = 0
|
|
90
|
+
for move in moves:
|
|
91
|
+
source = Path(move["to"])
|
|
92
|
+
destination = Path(move["from"])
|
|
93
|
+
if source.exists():
|
|
94
|
+
shutil.move(str(source), str(unique_path(destination)))
|
|
95
|
+
restored += 1
|
|
96
|
+
else:
|
|
97
|
+
print(f"Could not find, skipped: {source.name}")
|
|
98
|
+
log_file.unlink()
|
|
99
|
+
|
|
100
|
+
# Remove category folders that are now empty
|
|
101
|
+
for name in list(CATEGORIES) + ["Other"]:
|
|
102
|
+
category_dir = folder / name
|
|
103
|
+
if category_dir.is_dir() and not any(category_dir.iterdir()):
|
|
104
|
+
category_dir.rmdir()
|
|
105
|
+
|
|
106
|
+
print(f"Undone! {restored} file(s) moved back.")
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def main():
|
|
110
|
+
parser = argparse.ArgumentParser(
|
|
111
|
+
description="Sort a messy folder into neat sub-folders by file type."
|
|
112
|
+
)
|
|
113
|
+
parser.add_argument(
|
|
114
|
+
"folder",
|
|
115
|
+
nargs="?",
|
|
116
|
+
default=str(Path.home() / "Downloads"),
|
|
117
|
+
help="Folder to clean (default: your Downloads folder)",
|
|
118
|
+
)
|
|
119
|
+
parser.add_argument(
|
|
120
|
+
"--preview",
|
|
121
|
+
action="store_true",
|
|
122
|
+
help="Show what would move, without moving anything",
|
|
123
|
+
)
|
|
124
|
+
parser.add_argument(
|
|
125
|
+
"--undo",
|
|
126
|
+
action="store_true",
|
|
127
|
+
help="Move files back to where they were (undo the last cleanup)",
|
|
128
|
+
)
|
|
129
|
+
args = parser.parse_args()
|
|
130
|
+
|
|
131
|
+
folder = Path(args.folder)
|
|
132
|
+
if not folder.is_dir():
|
|
133
|
+
print(f"Folder not found: {folder}")
|
|
134
|
+
return
|
|
135
|
+
|
|
136
|
+
if args.undo:
|
|
137
|
+
undo(folder)
|
|
138
|
+
else:
|
|
139
|
+
clean(folder, args.preview)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
if __name__ == "__main__":
|
|
143
|
+
main()
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: downsweep
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Sort a messy Downloads folder into neat folders with one command. Includes preview and undo.
|
|
5
|
+
Author: Hari Prasath S
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/stonefrozen096-collab/downsweep
|
|
8
|
+
Project-URL: Source, https://github.com/stonefrozen096-collab/downsweep
|
|
9
|
+
Project-URL: Issues, https://github.com/stonefrozen096-collab/downsweep/issues
|
|
10
|
+
Keywords: downloads,organizer,cleaner,files,cli,undo
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Utilities
|
|
15
|
+
Requires-Python: >=3.8
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
\# downsweep
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
\*\*Sort a messy Downloads folder into neat folders with one command. Changed your mind? Undo it.\*\*
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
!\[PyPI](https://img.shields.io/pypi/v/downsweep) !\[License](https://img.shields.io/pypi/l/downsweep) !\[Python](https://img.shields.io/pypi/pyversions/downsweep)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
Your Downloads folder is full of PDFs, installers, screenshots and zip files
|
|
33
|
+
|
|
34
|
+
all mixed together. `downsweep` sorts them into Images, PDFs, Documents,
|
|
35
|
+
|
|
36
|
+
Programs and more in a few seconds, and lets you reverse it if you don't like
|
|
37
|
+
|
|
38
|
+
the result.
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
\## Install
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
pip install downsweep
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
\## Quick start
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
See what would happen first. Nothing is moved:
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
downsweep --preview
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
Clean your Downloads folder:
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
downsweep
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
Changed your mind? Move everything back:
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
downsweep --undo
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
Clean any other folder:
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
downsweep "C:\\path\\to\\folder"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
\## What you will see
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
Moved: report.pdf -> PDFs
|
|
91
|
+
|
|
92
|
+
Moved: photo.jpg -> Images
|
|
93
|
+
|
|
94
|
+
Moved: setup.exe -> Programs
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
Summary
|
|
99
|
+
|
|
100
|
+
-------
|
|
101
|
+
|
|
102
|
+
PDFs 40
|
|
103
|
+
|
|
104
|
+
Programs 30
|
|
105
|
+
|
|
106
|
+
Images 12
|
|
107
|
+
|
|
108
|
+
Total 82
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
Done! Changed your mind? Run: downsweep --undo
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
\## Features
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
| Feature | What it does |
|
|
121
|
+
|
|
122
|
+
| --- | --- |
|
|
123
|
+
|
|
124
|
+
| Preview mode | Shows what would move, changes nothing |
|
|
125
|
+
|
|
126
|
+
| Undo | Moves the last cleanup's files back where they were |
|
|
127
|
+
|
|
128
|
+
| Summary | Prints how many files went into each folder |
|
|
129
|
+
|
|
130
|
+
| Safe by design | Never overwrites a file, adds (1), (2) to duplicate names |
|
|
131
|
+
|
|
132
|
+
| Download-aware | Skips files still downloading, like .crdownload and .part |
|
|
133
|
+
|
|
134
|
+
| Zero dependencies | Only uses Python's standard library |
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
\## Folders it creates
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
Images, Videos, Audio, PDFs, Documents, Archives, Programs, Code and Other.
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
\## Good to know
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
\- Undo reverses the \*\*last\*\* cleanup of a folder only.
|
|
151
|
+
|
|
152
|
+
\- After a cleanup, a small file called `.downsweep\_log.json` is saved inside the
|
|
153
|
+
|
|
154
|
+
folder. That is how undo remembers what moved. Please don't delete it if you
|
|
155
|
+
|
|
156
|
+
want to undo.
|
|
157
|
+
|
|
158
|
+
\- Only files directly inside the folder are sorted. Existing sub-folders are
|
|
159
|
+
|
|
160
|
+
left alone.
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
\## License
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
MIT
|
|
169
|
+
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "downsweep"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Sort a messy Downloads folder into neat folders with one command. Includes preview and undo."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "Hari Prasath S" }]
|
|
14
|
+
keywords = ["downloads", "organizer", "cleaner", "files", "cli", "undo"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Environment :: Console",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Topic :: Utilities",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[project.urls]
|
|
23
|
+
Homepage = "https://github.com/stonefrozen096-collab/downsweep"
|
|
24
|
+
Source = "https://github.com/stonefrozen096-collab/downsweep"
|
|
25
|
+
Issues = "https://github.com/stonefrozen096-collab/downsweep/issues"
|
|
26
|
+
|
|
27
|
+
[project.scripts]
|
|
28
|
+
downsweep = "downsweep.cli:main"
|
|
29
|
+
|
|
30
|
+
[tool.setuptools]
|
|
31
|
+
packages = ["downsweep"]
|
downsweep-0.1.0/PKG-INFO
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: downsweep
|
|
3
|
-
Version: 0.1.0
|
|
4
|
-
Summary: Sort a messy Downloads folder into neat folders with one command.
|
|
5
|
-
Author: Hari Prasath S
|
|
6
|
-
Keywords: downloads,organizer,cleaner,files,cli
|
|
7
|
-
Requires-Python: >=3.8
|
|
8
|
-
Description-Content-Type: text/markdown
|
|
9
|
-
|
|
10
|
-
# downsweep
|
|
11
|
-
|
|
12
|
-
Sort a messy Downloads folder into neat folders with one command.
|
|
13
|
-
|
|
14
|
-
## Install
|
|
15
|
-
|
|
16
|
-
pip install downsweep
|
|
17
|
-
|
|
18
|
-
## Use
|
|
19
|
-
|
|
20
|
-
Clean your Downloads folder:
|
|
21
|
-
|
|
22
|
-
downsweep
|
|
23
|
-
|
|
24
|
-
See what would move first, without changing anything:
|
|
25
|
-
|
|
26
|
-
downsweep --preview
|
|
27
|
-
|
|
28
|
-
Clean any other folder:
|
|
29
|
-
|
|
30
|
-
downsweep "C:\path\to\folder"
|
|
31
|
-
|
|
32
|
-
## What it does
|
|
33
|
-
|
|
34
|
-
Files are moved into folders like Images, Videos, Audio, PDFs, Documents,
|
|
35
|
-
Archives, Programs, Code and Other. Files still being downloaded are skipped,
|
|
36
|
-
and existing files are never overwritten.
|
downsweep-0.1.0/README.md
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# downsweep
|
|
2
|
-
|
|
3
|
-
Sort a messy Downloads folder into neat folders with one command.
|
|
4
|
-
|
|
5
|
-
## Install
|
|
6
|
-
|
|
7
|
-
pip install downsweep
|
|
8
|
-
|
|
9
|
-
## Use
|
|
10
|
-
|
|
11
|
-
Clean your Downloads folder:
|
|
12
|
-
|
|
13
|
-
downsweep
|
|
14
|
-
|
|
15
|
-
See what would move first, without changing anything:
|
|
16
|
-
|
|
17
|
-
downsweep --preview
|
|
18
|
-
|
|
19
|
-
Clean any other folder:
|
|
20
|
-
|
|
21
|
-
downsweep "C:\path\to\folder"
|
|
22
|
-
|
|
23
|
-
## What it does
|
|
24
|
-
|
|
25
|
-
Files are moved into folders like Images, Videos, Audio, PDFs, Documents,
|
|
26
|
-
Archives, Programs, Code and Other. Files still being downloaded are skipped,
|
|
27
|
-
and existing files are never overwritten.
|
downsweep-0.1.0/downsweep/cli.py
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import argparse
|
|
2
|
-
import shutil
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
|
|
5
|
-
CATEGORIES = {
|
|
6
|
-
"Images": [".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".svg", ".avif", ".jfif"],
|
|
7
|
-
"Videos": [".mp4", ".mkv", ".avi", ".mov", ".webm"],
|
|
8
|
-
"Audio": [".mp3", ".wav", ".flac", ".m4a"],
|
|
9
|
-
"PDFs": [".pdf"],
|
|
10
|
-
"Documents": [".doc", ".docx", ".txt", ".ppt", ".pptx", ".xls", ".xlsx", ".csv"],
|
|
11
|
-
"Archives": [".zip", ".rar", ".7z", ".tar", ".gz"],
|
|
12
|
-
"Programs": [".exe", ".msi"],
|
|
13
|
-
"Code": [".py", ".js", ".html", ".htm", ".css", ".java", ".cpp", ".ipynb"],
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
# Files still being downloaded - never touch these
|
|
17
|
-
SKIP = {".crdownload", ".part", ".tmp", ".ini", ".lnk"}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def get_category(file):
|
|
21
|
-
ext = file.suffix.lower()
|
|
22
|
-
for name, extensions in CATEGORIES.items():
|
|
23
|
-
if ext in extensions:
|
|
24
|
-
return name
|
|
25
|
-
return "Other"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def unique_path(path):
|
|
29
|
-
# If a file with the same name exists, add (1), (2)... instead of overwriting
|
|
30
|
-
if not path.exists():
|
|
31
|
-
return path
|
|
32
|
-
counter = 1
|
|
33
|
-
while True:
|
|
34
|
-
new_path = path.with_name(f"{path.stem} ({counter}){path.suffix}")
|
|
35
|
-
if not new_path.exists():
|
|
36
|
-
return new_path
|
|
37
|
-
counter += 1
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def main():
|
|
41
|
-
parser = argparse.ArgumentParser(
|
|
42
|
-
description="Sort a messy folder into neat sub-folders by file type."
|
|
43
|
-
)
|
|
44
|
-
parser.add_argument(
|
|
45
|
-
"folder",
|
|
46
|
-
nargs="?",
|
|
47
|
-
default=str(Path.home() / "Downloads"),
|
|
48
|
-
help="Folder to clean (default: your Downloads folder)",
|
|
49
|
-
)
|
|
50
|
-
parser.add_argument(
|
|
51
|
-
"--preview",
|
|
52
|
-
action="store_true",
|
|
53
|
-
help="Show what would move, without moving anything",
|
|
54
|
-
)
|
|
55
|
-
args = parser.parse_args()
|
|
56
|
-
|
|
57
|
-
folder = Path(args.folder)
|
|
58
|
-
if not folder.is_dir():
|
|
59
|
-
print(f"Folder not found: {folder}")
|
|
60
|
-
return
|
|
61
|
-
|
|
62
|
-
count = 0
|
|
63
|
-
for item in folder.iterdir():
|
|
64
|
-
if not item.is_file() or item.suffix.lower() in SKIP:
|
|
65
|
-
continue
|
|
66
|
-
category = get_category(item)
|
|
67
|
-
if args.preview:
|
|
68
|
-
print(f"Would move: {item.name} -> {category}")
|
|
69
|
-
else:
|
|
70
|
-
target_dir = folder / category
|
|
71
|
-
target_dir.mkdir(exist_ok=True)
|
|
72
|
-
shutil.move(str(item), str(unique_path(target_dir / item.name)))
|
|
73
|
-
print(f"Moved: {item.name} -> {category}")
|
|
74
|
-
count += 1
|
|
75
|
-
|
|
76
|
-
if args.preview:
|
|
77
|
-
print(f"\n{count} file(s) would be moved. Nothing was changed.")
|
|
78
|
-
else:
|
|
79
|
-
print(f"\nDone! {count} file(s) sorted.")
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if __name__ == "__main__":
|
|
83
|
-
main()
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: downsweep
|
|
3
|
-
Version: 0.1.0
|
|
4
|
-
Summary: Sort a messy Downloads folder into neat folders with one command.
|
|
5
|
-
Author: Hari Prasath S
|
|
6
|
-
Keywords: downloads,organizer,cleaner,files,cli
|
|
7
|
-
Requires-Python: >=3.8
|
|
8
|
-
Description-Content-Type: text/markdown
|
|
9
|
-
|
|
10
|
-
# downsweep
|
|
11
|
-
|
|
12
|
-
Sort a messy Downloads folder into neat folders with one command.
|
|
13
|
-
|
|
14
|
-
## Install
|
|
15
|
-
|
|
16
|
-
pip install downsweep
|
|
17
|
-
|
|
18
|
-
## Use
|
|
19
|
-
|
|
20
|
-
Clean your Downloads folder:
|
|
21
|
-
|
|
22
|
-
downsweep
|
|
23
|
-
|
|
24
|
-
See what would move first, without changing anything:
|
|
25
|
-
|
|
26
|
-
downsweep --preview
|
|
27
|
-
|
|
28
|
-
Clean any other folder:
|
|
29
|
-
|
|
30
|
-
downsweep "C:\path\to\folder"
|
|
31
|
-
|
|
32
|
-
## What it does
|
|
33
|
-
|
|
34
|
-
Files are moved into folders like Images, Videos, Audio, PDFs, Documents,
|
|
35
|
-
Archives, Programs, Code and Other. Files still being downloaded are skipped,
|
|
36
|
-
and existing files are never overwritten.
|
downsweep-0.1.0/pyproject.toml
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
[build-system]
|
|
2
|
-
requires = ["setuptools>=68"]
|
|
3
|
-
build-backend = "setuptools.build_meta"
|
|
4
|
-
|
|
5
|
-
[project]
|
|
6
|
-
name = "downsweep"
|
|
7
|
-
version = "0.1.0"
|
|
8
|
-
description = "Sort a messy Downloads folder into neat folders with one command."
|
|
9
|
-
readme = "README.md"
|
|
10
|
-
requires-python = ">=3.8"
|
|
11
|
-
authors = [{ name = "Hari Prasath S" }]
|
|
12
|
-
keywords = ["downloads", "organizer", "cleaner", "files", "cli"]
|
|
13
|
-
|
|
14
|
-
[project.scripts]
|
|
15
|
-
downsweep = "downsweep.cli:main"
|
|
16
|
-
|
|
17
|
-
[tool.setuptools]
|
|
18
|
-
packages = ["downsweep"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|