mfget 0.1.1__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.
- mfget-0.1.1/LICENSE +21 -0
- mfget-0.1.1/PKG-INFO +235 -0
- mfget-0.1.1/README.md +210 -0
- mfget-0.1.1/mediafire_dl/__init__.py +3 -0
- mfget-0.1.1/mediafire_dl/__main__.py +9 -0
- mfget-0.1.1/mediafire_dl/cli.py +112 -0
- mfget-0.1.1/mediafire_dl/downloader.py +61 -0
- mfget-0.1.1/mediafire_dl/errors.py +18 -0
- mfget-0.1.1/mediafire_dl/http.py +118 -0
- mfget-0.1.1/mediafire_dl/mediafire.py +159 -0
- mfget-0.1.1/mediafire_dl/models.py +30 -0
- mfget-0.1.1/mediafire_dl/output.py +172 -0
- mfget-0.1.1/mfget.egg-info/PKG-INFO +235 -0
- mfget-0.1.1/mfget.egg-info/SOURCES.txt +18 -0
- mfget-0.1.1/mfget.egg-info/dependency_links.txt +1 -0
- mfget-0.1.1/mfget.egg-info/entry_points.txt +2 -0
- mfget-0.1.1/mfget.egg-info/requires.txt +1 -0
- mfget-0.1.1/mfget.egg-info/top_level.txt +1 -0
- mfget-0.1.1/pyproject.toml +39 -0
- mfget-0.1.1/setup.cfg +4 -0
mfget-0.1.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mediafire-DL contributors
|
|
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.
|
mfget-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mfget
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: A clean MediaFire file and folder downloader with a Rich-powered CLI.
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/worstgirlinamerica/mediafire-dl
|
|
7
|
+
Project-URL: Repository, https://github.com/worstgirlinamerica/mediafire-dl
|
|
8
|
+
Project-URL: Issues, https://github.com/worstgirlinamerica/mediafire-dl/issues
|
|
9
|
+
Keywords: mediafire,downloader,cli,rich
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
19
|
+
Classifier: Topic :: Utilities
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: rich>=13.7.0
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# Mediafire-DL
|
|
27
|
+
|
|
28
|
+
Mediafire-DL is a small command-line downloader for MediaFire links.
|
|
29
|
+
|
|
30
|
+
It handles single files and folders, keeps nested folder structure intact, and shows a clean progress display while it downloads. Public links work without any login details. Links that require an account can be tried with an optional local cookies file.
|
|
31
|
+
|
|
32
|
+
## What It Does
|
|
33
|
+
|
|
34
|
+
- Downloads public MediaFire file links
|
|
35
|
+
- Downloads public MediaFire folder links
|
|
36
|
+
- Walks nested folders automatically
|
|
37
|
+
- Saves folders using the same folder layout MediaFire reports
|
|
38
|
+
- Shows a file preview before downloading
|
|
39
|
+
- Supports a dry-run mode so you can check what would be saved first
|
|
40
|
+
- Defaults to your system `Downloads` folder
|
|
41
|
+
- Can read a local `cookies.txt` file for links your own browser account can access
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
You need Python 3.9 or newer.
|
|
46
|
+
|
|
47
|
+
### From GitHub
|
|
48
|
+
|
|
49
|
+
This works without cloning the repo first:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
python3 -m pip install --user git+https://github.com/worstgirlinamerica/mediafire-dl.git
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Then run:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
mediafire-dl
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
On Windows, use `py` instead of `python3`:
|
|
62
|
+
|
|
63
|
+
```powershell
|
|
64
|
+
py -m pip install --user git+https://github.com/worstgirlinamerica/mediafire-dl.git
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### From PyPI
|
|
68
|
+
|
|
69
|
+
Once Mediafire-DL is published to PyPI as `mfget`, it can be installed like this:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
python3 -m pip install --user mfget
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
On Windows:
|
|
76
|
+
|
|
77
|
+
```powershell
|
|
78
|
+
py -m pip install --user mfget
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### macOS
|
|
82
|
+
|
|
83
|
+
If you installed with Python 3:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
mediafire-dl
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
If your terminal says `mediafire-dl` was not found, use the module form:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
python3 -m mediafire_dl
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
If the command is installed but your shell cannot find it, add Python's user script folder to your PATH. The version number may be different on your machine:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
echo 'export PATH="$HOME/Library/Python/3.12/bin:$PATH"' >> ~/.zshrc
|
|
99
|
+
source ~/.zshrc
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Windows
|
|
103
|
+
|
|
104
|
+
Run:
|
|
105
|
+
|
|
106
|
+
```powershell
|
|
107
|
+
mediafire-dl
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
If PowerShell says `mediafire-dl` was not found, use:
|
|
111
|
+
|
|
112
|
+
```powershell
|
|
113
|
+
py -m mediafire_dl
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
If you want the `mediafire-dl` command to work directly, add Python's user Scripts folder to PATH. It usually looks like this, with the Python version adjusted for your install:
|
|
117
|
+
|
|
118
|
+
```text
|
|
119
|
+
%APPDATA%\Python\Python312\Scripts
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Linux
|
|
123
|
+
|
|
124
|
+
Run:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
mediafire-dl
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
If your shell says `mediafire-dl` was not found, use:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
python3 -m mediafire_dl
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Or add Python's user script folder to your PATH:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
|
|
140
|
+
source ~/.bashrc
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Local Development
|
|
144
|
+
|
|
145
|
+
If you cloned the repo and want to install from the project folder:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
python3 -m pip install --user .
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Usage
|
|
152
|
+
|
|
153
|
+
Run the command and paste a MediaFire link when it asks:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
mediafire-dl
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Or pass the link directly:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
mediafire-dl "https://www.mediafire.com/file/example/file.zip/file"
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Choose a save folder:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
mediafire-dl "https://www.mediafire.com/folder/example/My+Folder" --output ~/Downloads/MediaFire
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Preview a folder without downloading anything:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
mediafire-dl --dry-run "https://www.mediafire.com/folder/example/My+Folder"
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Show more technical error details:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
mediafire-dl --verbose "https://www.mediafire.com/file/example/file.zip/file"
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Use a local cookies file for a link that your browser account can already access:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
mediafire-dl --cookies ~/Downloads/cookies.txt "https://www.mediafire.com/file/example/file.zip/file"
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
The cookies file must be in Netscape/Mozilla `cookies.txt` format. Mediafire-DL only reads the file from your computer for that run.
|
|
190
|
+
|
|
191
|
+
## Supported Links
|
|
192
|
+
|
|
193
|
+
Supported:
|
|
194
|
+
|
|
195
|
+
- Public MediaFire file links
|
|
196
|
+
- Public MediaFire folder links
|
|
197
|
+
- Public folders with nested subfolders
|
|
198
|
+
- MediaFire file or folder links that work with a user-provided `cookies.txt` file
|
|
199
|
+
|
|
200
|
+
Not supported:
|
|
201
|
+
|
|
202
|
+
- Asking for or storing MediaFire usernames and passwords
|
|
203
|
+
- Automatically reading cookies from your browser
|
|
204
|
+
- Password-protected downloads that require an extra password prompt
|
|
205
|
+
- Captcha-gated or blocked downloads
|
|
206
|
+
|
|
207
|
+
## Privacy And Safety
|
|
208
|
+
|
|
209
|
+
Mediafire-DL does not ask for MediaFire login details and does not read browser cookies automatically. If you use `--cookies`, the cookie file stays on your machine and is only read by the current command.
|
|
210
|
+
|
|
211
|
+
This repo already ignores common private and generated files in `.gitignore`, including `.env`, cookies, logs, caches, virtual environments, `.DS_Store`, partial downloads, and local media/download folders.
|
|
212
|
+
|
|
213
|
+
## Development
|
|
214
|
+
|
|
215
|
+
For an editable install while working on the code:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
python3 -m pip install --user -e .
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Run the CLI from source:
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
python3 -m mediafire_dl
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
The package entry point is:
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
mediafire-dl
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## License
|
|
234
|
+
|
|
235
|
+
MIT License. See [LICENSE](LICENSE).
|
mfget-0.1.1/README.md
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# Mediafire-DL
|
|
2
|
+
|
|
3
|
+
Mediafire-DL is a small command-line downloader for MediaFire links.
|
|
4
|
+
|
|
5
|
+
It handles single files and folders, keeps nested folder structure intact, and shows a clean progress display while it downloads. Public links work without any login details. Links that require an account can be tried with an optional local cookies file.
|
|
6
|
+
|
|
7
|
+
## What It Does
|
|
8
|
+
|
|
9
|
+
- Downloads public MediaFire file links
|
|
10
|
+
- Downloads public MediaFire folder links
|
|
11
|
+
- Walks nested folders automatically
|
|
12
|
+
- Saves folders using the same folder layout MediaFire reports
|
|
13
|
+
- Shows a file preview before downloading
|
|
14
|
+
- Supports a dry-run mode so you can check what would be saved first
|
|
15
|
+
- Defaults to your system `Downloads` folder
|
|
16
|
+
- Can read a local `cookies.txt` file for links your own browser account can access
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
You need Python 3.9 or newer.
|
|
21
|
+
|
|
22
|
+
### From GitHub
|
|
23
|
+
|
|
24
|
+
This works without cloning the repo first:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
python3 -m pip install --user git+https://github.com/worstgirlinamerica/mediafire-dl.git
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Then run:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
mediafire-dl
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
On Windows, use `py` instead of `python3`:
|
|
37
|
+
|
|
38
|
+
```powershell
|
|
39
|
+
py -m pip install --user git+https://github.com/worstgirlinamerica/mediafire-dl.git
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### From PyPI
|
|
43
|
+
|
|
44
|
+
Once Mediafire-DL is published to PyPI as `mfget`, it can be installed like this:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
python3 -m pip install --user mfget
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
On Windows:
|
|
51
|
+
|
|
52
|
+
```powershell
|
|
53
|
+
py -m pip install --user mfget
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### macOS
|
|
57
|
+
|
|
58
|
+
If you installed with Python 3:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
mediafire-dl
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
If your terminal says `mediafire-dl` was not found, use the module form:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
python3 -m mediafire_dl
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
If the command is installed but your shell cannot find it, add Python's user script folder to your PATH. The version number may be different on your machine:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
echo 'export PATH="$HOME/Library/Python/3.12/bin:$PATH"' >> ~/.zshrc
|
|
74
|
+
source ~/.zshrc
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Windows
|
|
78
|
+
|
|
79
|
+
Run:
|
|
80
|
+
|
|
81
|
+
```powershell
|
|
82
|
+
mediafire-dl
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
If PowerShell says `mediafire-dl` was not found, use:
|
|
86
|
+
|
|
87
|
+
```powershell
|
|
88
|
+
py -m mediafire_dl
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
If you want the `mediafire-dl` command to work directly, add Python's user Scripts folder to PATH. It usually looks like this, with the Python version adjusted for your install:
|
|
92
|
+
|
|
93
|
+
```text
|
|
94
|
+
%APPDATA%\Python\Python312\Scripts
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Linux
|
|
98
|
+
|
|
99
|
+
Run:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
mediafire-dl
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
If your shell says `mediafire-dl` was not found, use:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
python3 -m mediafire_dl
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Or add Python's user script folder to your PATH:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
|
|
115
|
+
source ~/.bashrc
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Local Development
|
|
119
|
+
|
|
120
|
+
If you cloned the repo and want to install from the project folder:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
python3 -m pip install --user .
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Usage
|
|
127
|
+
|
|
128
|
+
Run the command and paste a MediaFire link when it asks:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
mediafire-dl
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Or pass the link directly:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
mediafire-dl "https://www.mediafire.com/file/example/file.zip/file"
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Choose a save folder:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
mediafire-dl "https://www.mediafire.com/folder/example/My+Folder" --output ~/Downloads/MediaFire
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Preview a folder without downloading anything:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
mediafire-dl --dry-run "https://www.mediafire.com/folder/example/My+Folder"
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Show more technical error details:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
mediafire-dl --verbose "https://www.mediafire.com/file/example/file.zip/file"
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Use a local cookies file for a link that your browser account can already access:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
mediafire-dl --cookies ~/Downloads/cookies.txt "https://www.mediafire.com/file/example/file.zip/file"
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The cookies file must be in Netscape/Mozilla `cookies.txt` format. Mediafire-DL only reads the file from your computer for that run.
|
|
165
|
+
|
|
166
|
+
## Supported Links
|
|
167
|
+
|
|
168
|
+
Supported:
|
|
169
|
+
|
|
170
|
+
- Public MediaFire file links
|
|
171
|
+
- Public MediaFire folder links
|
|
172
|
+
- Public folders with nested subfolders
|
|
173
|
+
- MediaFire file or folder links that work with a user-provided `cookies.txt` file
|
|
174
|
+
|
|
175
|
+
Not supported:
|
|
176
|
+
|
|
177
|
+
- Asking for or storing MediaFire usernames and passwords
|
|
178
|
+
- Automatically reading cookies from your browser
|
|
179
|
+
- Password-protected downloads that require an extra password prompt
|
|
180
|
+
- Captcha-gated or blocked downloads
|
|
181
|
+
|
|
182
|
+
## Privacy And Safety
|
|
183
|
+
|
|
184
|
+
Mediafire-DL does not ask for MediaFire login details and does not read browser cookies automatically. If you use `--cookies`, the cookie file stays on your machine and is only read by the current command.
|
|
185
|
+
|
|
186
|
+
This repo already ignores common private and generated files in `.gitignore`, including `.env`, cookies, logs, caches, virtual environments, `.DS_Store`, partial downloads, and local media/download folders.
|
|
187
|
+
|
|
188
|
+
## Development
|
|
189
|
+
|
|
190
|
+
For an editable install while working on the code:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
python3 -m pip install --user -e .
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Run the CLI from source:
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
python3 -m mediafire_dl
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
The package entry point is:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
mediafire-dl
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## License
|
|
209
|
+
|
|
210
|
+
MIT License. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import logging
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
|
|
10
|
+
from .downloader import Downloader
|
|
11
|
+
from .errors import MediafireDLError
|
|
12
|
+
from .http import HttpClient
|
|
13
|
+
from .mediafire import MediafireClient
|
|
14
|
+
from .output import RichUI
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def main(argv: list[str] | None = None) -> int:
|
|
18
|
+
args = parse_args(argv)
|
|
19
|
+
configure_logging(args.verbose)
|
|
20
|
+
|
|
21
|
+
ui = RichUI(Console())
|
|
22
|
+
link = args.link or ui.ask_link()
|
|
23
|
+
if not link.strip():
|
|
24
|
+
ui.error("Please paste a MediaFire file or folder link.")
|
|
25
|
+
return 1
|
|
26
|
+
destination = Path(args.output or ui.ask_save_dir(str(default_downloads_dir()))).expanduser()
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
cookie_file = Path(args.cookies).expanduser() if args.cookies else None
|
|
30
|
+
http = HttpClient(cookie_file=cookie_file)
|
|
31
|
+
client = MediafireClient(http)
|
|
32
|
+
downloader = Downloader(http)
|
|
33
|
+
|
|
34
|
+
ui.status("Finding files...")
|
|
35
|
+
plan = client.build_plan(link)
|
|
36
|
+
if not plan.files:
|
|
37
|
+
ui.warning("No downloadable files were found.")
|
|
38
|
+
return 1
|
|
39
|
+
|
|
40
|
+
ui.show_plan(plan)
|
|
41
|
+
ui.status("Preparing downloads...")
|
|
42
|
+
|
|
43
|
+
if args.dry_run:
|
|
44
|
+
ui.warning("Dry run: nothing was downloaded.")
|
|
45
|
+
return 0
|
|
46
|
+
|
|
47
|
+
ui.status("Downloading...")
|
|
48
|
+
saved_to = downloader.download_plan(plan, destination, ui)
|
|
49
|
+
ui.finished(str(saved_to))
|
|
50
|
+
return 0
|
|
51
|
+
except KeyboardInterrupt:
|
|
52
|
+
ui.error("Download cancelled.")
|
|
53
|
+
return 130
|
|
54
|
+
except MediafireDLError as exc:
|
|
55
|
+
if args.verbose:
|
|
56
|
+
logging.getLogger(__name__).exception("Mediafire-DL failed")
|
|
57
|
+
ui.error(str(exc))
|
|
58
|
+
return 1
|
|
59
|
+
except Exception as exc:
|
|
60
|
+
if args.verbose:
|
|
61
|
+
logging.getLogger(__name__).exception("Unexpected failure")
|
|
62
|
+
ui.error(str(exc))
|
|
63
|
+
else:
|
|
64
|
+
ui.error("Something went wrong. Run again with --verbose for details.")
|
|
65
|
+
return 1
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
|
69
|
+
parser = argparse.ArgumentParser(
|
|
70
|
+
prog="mediafire-dl",
|
|
71
|
+
description="Download MediaFire files and folders with a clean progress UI.",
|
|
72
|
+
)
|
|
73
|
+
parser.add_argument("link", nargs="?", help="MediaFire file or folder link")
|
|
74
|
+
parser.add_argument(
|
|
75
|
+
"-o",
|
|
76
|
+
"--output",
|
|
77
|
+
help="Folder to save downloads into. Defaults to your Downloads folder.",
|
|
78
|
+
)
|
|
79
|
+
parser.add_argument(
|
|
80
|
+
"--dry-run",
|
|
81
|
+
action="store_true",
|
|
82
|
+
help="List files without downloading them.",
|
|
83
|
+
)
|
|
84
|
+
parser.add_argument(
|
|
85
|
+
"--cookies",
|
|
86
|
+
help=(
|
|
87
|
+
"Path to a Netscape/Mozilla cookies.txt file to use for links your browser can access. "
|
|
88
|
+
"Cookies are read locally and are not saved by Mediafire-DL."
|
|
89
|
+
),
|
|
90
|
+
)
|
|
91
|
+
parser.add_argument(
|
|
92
|
+
"--verbose",
|
|
93
|
+
action="store_true",
|
|
94
|
+
help="Show technical logs and tracebacks for troubleshooting.",
|
|
95
|
+
)
|
|
96
|
+
return parser.parse_args(argv)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def configure_logging(verbose: bool) -> None:
|
|
100
|
+
logging.basicConfig(
|
|
101
|
+
level=logging.DEBUG if verbose else logging.CRITICAL,
|
|
102
|
+
format="%(levelname)s: %(name)s: %(message)s",
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def default_downloads_dir() -> Path:
|
|
107
|
+
downloads = Path.home() / "Downloads"
|
|
108
|
+
return downloads if downloads.exists() else Path.home()
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
if __name__ == "__main__":
|
|
112
|
+
sys.exit(main())
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from .errors import DownloadLinkError, MediafireDLError
|
|
7
|
+
from .http import HttpClient
|
|
8
|
+
from .mediafire import safe_name
|
|
9
|
+
from .models import DownloadPlan, MediafireItem
|
|
10
|
+
from .output import UserInterface
|
|
11
|
+
|
|
12
|
+
LOGGER = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Downloader:
|
|
16
|
+
def __init__(self, http: HttpClient | None = None, chunk_size: int = 1024 * 128) -> None:
|
|
17
|
+
self.http = http or HttpClient()
|
|
18
|
+
self.chunk_size = chunk_size
|
|
19
|
+
|
|
20
|
+
def download_plan(self, plan: DownloadPlan, destination: Path, ui: UserInterface) -> Path:
|
|
21
|
+
root = destination / safe_name(plan.display_name) if plan.is_folder else destination
|
|
22
|
+
root.mkdir(parents=True, exist_ok=True)
|
|
23
|
+
|
|
24
|
+
total = len(plan.files)
|
|
25
|
+
for index, item in enumerate(plan.files, start=1):
|
|
26
|
+
self._download_item(item, root, ui, index, total)
|
|
27
|
+
return root
|
|
28
|
+
|
|
29
|
+
def _download_item(
|
|
30
|
+
self,
|
|
31
|
+
item: MediafireItem,
|
|
32
|
+
root: Path,
|
|
33
|
+
ui: UserInterface,
|
|
34
|
+
index: int,
|
|
35
|
+
total: int,
|
|
36
|
+
) -> None:
|
|
37
|
+
target = root / Path(*item.relative_folder.parts) / safe_name(item.name)
|
|
38
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
39
|
+
partial = target.with_name(f"{target.name}.part")
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
direct_url = self.http.find_public_download_url(item.page_url)
|
|
43
|
+
except MediafireDLError:
|
|
44
|
+
raise
|
|
45
|
+
except Exception as exc:
|
|
46
|
+
LOGGER.debug("Unexpected error while finding download URL", exc_info=exc)
|
|
47
|
+
raise DownloadLinkError(f"Could not prepare {item.name} for download.") from exc
|
|
48
|
+
|
|
49
|
+
with self.http.open_download(direct_url) as response:
|
|
50
|
+
length = response.headers.get("Content-Length")
|
|
51
|
+
total_size = int(length) if length and length.isdigit() else item.size
|
|
52
|
+
progress = ui.start_file(index, total, item, total_size)
|
|
53
|
+
with partial.open("wb") as file:
|
|
54
|
+
while True:
|
|
55
|
+
chunk = response.read(self.chunk_size)
|
|
56
|
+
if not chunk:
|
|
57
|
+
break
|
|
58
|
+
file.write(chunk)
|
|
59
|
+
progress.update(len(chunk))
|
|
60
|
+
progress.finish()
|
|
61
|
+
partial.replace(target)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
class MediafireDLError(Exception):
|
|
2
|
+
"""Base exception for user-facing downloader errors."""
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class UnsupportedLinkError(MediafireDLError):
|
|
6
|
+
"""Raised when a link is not a supported MediaFire file or folder URL."""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class MediafireAPIError(MediafireDLError):
|
|
10
|
+
"""Raised when MediaFire's public API returns an error."""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class DownloadLinkError(MediafireDLError):
|
|
14
|
+
"""Raised when a public download URL cannot be extracted."""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class CookieFileError(MediafireDLError):
|
|
18
|
+
"""Raised when a user-provided cookie file cannot be loaded."""
|