Open-AutoTools 0.0.3rc3__py3-none-any.whl → 0.0.3rc5__py3-none-any.whl
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.
- {Open_AutoTools-0.0.3rc3.dist-info → Open_AutoTools-0.0.3rc5.dist-info}/METADATA +76 -57
- {Open_AutoTools-0.0.3rc3.dist-info → Open_AutoTools-0.0.3rc5.dist-info}/RECORD +8 -8
- autotools/autodownload/core.py +132 -26
- autotools/test/commands.py +15 -17
- {Open_AutoTools-0.0.3rc3.dist-info → Open_AutoTools-0.0.3rc5.dist-info}/LICENSE +0 -0
- {Open_AutoTools-0.0.3rc3.dist-info → Open_AutoTools-0.0.3rc5.dist-info}/WHEEL +0 -0
- {Open_AutoTools-0.0.3rc3.dist-info → Open_AutoTools-0.0.3rc5.dist-info}/entry_points.txt +0 -0
- {Open_AutoTools-0.0.3rc3.dist-info → Open_AutoTools-0.0.3rc5.dist-info}/top_level.txt +0 -0
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: Open-AutoTools
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.3rc5
|
|
4
4
|
Summary: A suite of automated tools accessible via CLI with a simple `autotools` command
|
|
5
5
|
Home-page: https://github.com/BabylooPro/Open-AutoTools
|
|
6
6
|
Author: BabylooPro
|
|
7
7
|
Author-email: maxremy.dev@gmail.com
|
|
8
8
|
Project-URL: Bug Tracker, https://github.com/BabylooPro/Open-AutoTools/issues
|
|
9
|
-
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
10
12
|
Classifier: License :: OSI Approved :: MIT License
|
|
11
13
|
Classifier: Operating System :: OS Independent
|
|
12
|
-
Requires-Python: >=3.
|
|
14
|
+
Requires-Python: >=3.10
|
|
13
15
|
Description-Content-Type: text/markdown
|
|
14
16
|
License-File: LICENSE
|
|
15
17
|
Requires-Dist: Brotli==1.1.0
|
|
@@ -54,6 +56,9 @@ Requires-Dist: ffmpeg-python>=0.2.0
|
|
|
54
56
|
Provides-Extra: test
|
|
55
57
|
Requires-Dist: pytest>=7.4.0; extra == "test"
|
|
56
58
|
Requires-Dist: pytest-cov>=4.1.0; extra == "test"
|
|
59
|
+
Requires-Dist: pytest-sugar>=1.0.0; extra == "test"
|
|
60
|
+
Requires-Dist: pytest-xdist>=3.5.0; extra == "test"
|
|
61
|
+
Requires-Dist: pytest-timeout>=2.2.0; extra == "test"
|
|
57
62
|
Dynamic: author
|
|
58
63
|
Dynamic: author-email
|
|
59
64
|
Dynamic: classifier
|
|
@@ -76,8 +81,9 @@ Dynamic: summary
|
|
|
76
81
|
[CHANGELOG_URL]: CHANGELOG.md
|
|
77
82
|
[TODO_BADGE]: https://img.shields.io/badge/TODO-purple.svg
|
|
78
83
|
[TODO_URL]: TODO.md
|
|
84
|
+
[TOTAL_STABILITY]: https://img.shields.io/badge/Total%20Stability-73%25-yellow
|
|
79
85
|
|
|
80
|
-
[![PyPI][PYPI_BADGE]][PYPI_URL] [![Python][PYTHON_BADGE]][PYTHON_URL] [![CHANGELOG][CHANGELOG_BADGE]][CHANGELOG_URL] [![TODO][TODO_BADGE]][TODO_URL]
|
|
86
|
+
[![PyPI][PYPI_BADGE]][PYPI_URL] [![Python][PYTHON_BADGE]][PYTHON_URL] [![CHANGELOG][CHANGELOG_BADGE]][CHANGELOG_URL] [![TODO][TODO_BADGE]][TODO_URL] ![Total Stability][TOTAL_STABILITY]
|
|
81
87
|
|
|
82
88
|
Open-AutoTools is a comprehensive Python CLI toolkit that streamlines everyday developer tasks through a collection of powerful command-line utilities. Each tool is designed to enhance productivity directly from your terminal.
|
|
83
89
|
|
|
@@ -108,11 +114,15 @@ pip install -r requirements.txt
|
|
|
108
114
|
|
|
109
115
|
# For development, install in editable mode
|
|
110
116
|
pip install -e .
|
|
117
|
+
|
|
118
|
+
# INFO: if you want to run tests and some errors occur before see test executable
|
|
119
|
+
# install test dependencies directly (optional)
|
|
120
|
+
pip install -e ".[test]"
|
|
111
121
|
```
|
|
112
122
|
|
|
113
123
|
## Key Features
|
|
114
124
|
|
|
115
|
-
### AutoCaps
|
|
125
|
+
### AutoCaps ![Stability][AUTOCAPS_EFF]
|
|
116
126
|
|
|
117
127
|
- **Description:** Converts any text entered by the user to uppercase.
|
|
118
128
|
- **Usage:**
|
|
@@ -124,7 +134,7 @@ pip install -e .
|
|
|
124
134
|
YOUR TEXT HERE.
|
|
125
135
|
```
|
|
126
136
|
|
|
127
|
-
### AutoLower
|
|
137
|
+
### AutoLower ![Stability][AUTOLOWER_EFF]
|
|
128
138
|
|
|
129
139
|
- **Description:** Converts any text entered by the user to lowercase.
|
|
130
140
|
- **Usage:**
|
|
@@ -136,7 +146,7 @@ pip install -e .
|
|
|
136
146
|
your text here.
|
|
137
147
|
```
|
|
138
148
|
|
|
139
|
-
### AutoPassword
|
|
149
|
+
### AutoPassword ![Stability][AUTOPASSWORD_EFF]
|
|
140
150
|
|
|
141
151
|
- **Description:** Generates secure random passwords and encryption keys with customizable options.
|
|
142
152
|
- **Usage:**
|
|
@@ -147,6 +157,7 @@ pip install -e .
|
|
|
147
157
|
~ ❯ autopassword --password-key "your-password" --analyze
|
|
148
158
|
```
|
|
149
159
|
- **Options:**
|
|
160
|
+
|
|
150
161
|
- `--length, -l`: Set password length (default: 12)
|
|
151
162
|
- `--no-uppercase, -u`: Exclude uppercase letters
|
|
152
163
|
- `--no-numbers, -n`: Exclude numbers
|
|
@@ -157,54 +168,7 @@ pip install -e .
|
|
|
157
168
|
- `--gen-key, -g`: Generate a random encryption key
|
|
158
169
|
- `--password-key, -p`: Generate an encryption key from password
|
|
159
170
|
|
|
160
|
-
###
|
|
161
|
-
|
|
162
|
-
- **Description:** Translates text between languages with automatic source language detection.
|
|
163
|
-
- **Usage:**
|
|
164
|
-
|
|
165
|
-
```
|
|
166
|
-
~ ❯ autotranslate "Bonjour le monde" --to en
|
|
167
|
-
Hello world
|
|
168
|
-
|
|
169
|
-
~ ❯ autotranslate "Hello world" --to fr --copy
|
|
170
|
-
Bonjour le monde
|
|
171
|
-
// Result also copied to clipboard
|
|
172
|
-
|
|
173
|
-
~ ❯ autotranslate "こんにちは" --to en --detect
|
|
174
|
-
[Detected: ja] Hello
|
|
175
|
-
|
|
176
|
-
~ ❯ autotranslate --list-languages
|
|
177
|
-
// Shows all supported languages
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
- **Options:**
|
|
181
|
-
- `--to`: Target language code (default: en)
|
|
182
|
-
- `--from`: Source language code (default: auto-detect)
|
|
183
|
-
- `--copy`: Copy translation to clipboard
|
|
184
|
-
- `--detect`: Show detected source language
|
|
185
|
-
- `--list-languages`: Show all supported language codes and names
|
|
186
|
-
- `--output, -o`: Save translation to file
|
|
187
|
-
|
|
188
|
-
### AutoSpell (unreleased)
|
|
189
|
-
|
|
190
|
-
- **Description:** Checks and corrects spelling in text with multi-language support.
|
|
191
|
-
- **Usage:**
|
|
192
|
-
```
|
|
193
|
-
~ ❯ autospell "Your text with misspellings"
|
|
194
|
-
~ ❯ autospell --lang fr "Votre texte avec des fautes"
|
|
195
|
-
~ ❯ autospell --fix "Text to autocorrect"
|
|
196
|
-
```
|
|
197
|
-
- **Options:**
|
|
198
|
-
- `--lang, -l`: Language code (default: auto)
|
|
199
|
-
- `--fix, -f`: Auto-fix text and copy to clipboard
|
|
200
|
-
- `--copy, -c`: Copy result to clipboard
|
|
201
|
-
- `--list-languages`: Show supported languages
|
|
202
|
-
- `--json, -j`: Output results as JSON
|
|
203
|
-
- `--ignore, -i`: Error types to ignore (spelling/grammar/style/punctuation)
|
|
204
|
-
- `--interactive, -n`: Interactive mode - confirm each correction
|
|
205
|
-
- `--output, -o`: Save corrections to file
|
|
206
|
-
|
|
207
|
-
### AutoDownload
|
|
171
|
+
### AutoDownload ![Stability][AUTODOWNLOAD_EFF]
|
|
208
172
|
|
|
209
173
|
- **Description:** Downloads videos from YouTube and files from other sources.
|
|
210
174
|
- **Usage:**
|
|
@@ -226,7 +190,7 @@ pip install -e .
|
|
|
226
190
|
- **Features:**
|
|
227
191
|
|
|
228
192
|
- Automatic bot detection bypass
|
|
229
|
-
- Mobile API integration for better
|
|
193
|
+
- Mobile API integration for better Stability
|
|
230
194
|
- Progress tracking with detailed status
|
|
231
195
|
- Multiple quality options
|
|
232
196
|
- MP3 audio extraction
|
|
@@ -245,7 +209,7 @@ pip install -e .
|
|
|
245
209
|
|
|
246
210
|
> **Note:** The tool uses YouTube's mobile API for better compatibility and reliability.
|
|
247
211
|
|
|
248
|
-
### AutoIP
|
|
212
|
+
### AutoIP ![Stability][AUTOIP_EFF]
|
|
249
213
|
|
|
250
214
|
- **Description:** Displays network information including IP addresses, connectivity tests, speed tests, and more.
|
|
251
215
|
- **Usage:**
|
|
@@ -278,6 +242,53 @@ pip install -e .
|
|
|
278
242
|
- DNS server information
|
|
279
243
|
- IP geolocation
|
|
280
244
|
|
|
245
|
+
### AutoTranslate ![Stability][AUTOTRANSLATE_EFF]
|
|
246
|
+
|
|
247
|
+
- **Description:** Translates text between languages with automatic source language detection.
|
|
248
|
+
- **Usage:**
|
|
249
|
+
|
|
250
|
+
```
|
|
251
|
+
~ ❯ autotranslate "Bonjour le monde" --to en
|
|
252
|
+
Hello world
|
|
253
|
+
|
|
254
|
+
~ ❯ autotranslate "Hello world" --to fr --copy
|
|
255
|
+
Bonjour le monde
|
|
256
|
+
// Result also copied to clipboard
|
|
257
|
+
|
|
258
|
+
~ ❯ autotranslate "こんにちは" --to en --detect
|
|
259
|
+
[Detected: ja] Hello
|
|
260
|
+
|
|
261
|
+
~ ❯ autotranslate --list-languages
|
|
262
|
+
// Shows all supported languages
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
- **Options:**
|
|
266
|
+
- `--to`: Target language code (default: en)
|
|
267
|
+
- `--from`: Source language code (default: auto-detect)
|
|
268
|
+
- `--copy`: Copy translation to clipboard
|
|
269
|
+
- `--detect`: Show detected source language
|
|
270
|
+
- `--list-languages`: Show all supported language codes and names
|
|
271
|
+
- `--output, -o`: Save translation to file
|
|
272
|
+
|
|
273
|
+
### AutoSpell (unreleased) ![Stability][AUTOSPELL_EFF]
|
|
274
|
+
|
|
275
|
+
- **Description:** Checks and corrects spelling in text with multi-language support.
|
|
276
|
+
- **Usage:**
|
|
277
|
+
```
|
|
278
|
+
~ ❯ autospell "Your text with misspellings"
|
|
279
|
+
~ ❯ autospell --lang fr "Votre texte avec des fautes"
|
|
280
|
+
~ ❯ autospell --fix "Text to autocorrect"
|
|
281
|
+
```
|
|
282
|
+
- **Options:**
|
|
283
|
+
- `--lang, -l`: Language code (default: auto)
|
|
284
|
+
- `--fix, -f`: Auto-fix text and copy to clipboard
|
|
285
|
+
- `--copy, -c`: Copy result to clipboard
|
|
286
|
+
- `--list-languages`: Show supported languages
|
|
287
|
+
- `--json, -j`: Output results as JSON
|
|
288
|
+
- `--ignore, -i`: Error types to ignore (spelling/grammar/style/punctuation)
|
|
289
|
+
- `--interactive, -n`: Interactive mode - confirm each correction
|
|
290
|
+
- `--output, -o`: Save corrections to file
|
|
291
|
+
|
|
281
292
|
### Test Suite (DEVELOPMENT ONLY)
|
|
282
293
|
|
|
283
294
|
- **Description:** Run the test suite for Open-AutoTools
|
|
@@ -296,3 +307,11 @@ pip install -e .
|
|
|
296
307
|
## License
|
|
297
308
|
|
|
298
309
|
This project is licensed under the MIT License. For more details, see the [LICENSE](LICENSE) file.
|
|
310
|
+
|
|
311
|
+
[AUTOCAPS_EFF]: https://img.shields.io/badge/Stability-99%25-success
|
|
312
|
+
[AUTOLOWER_EFF]: https://img.shields.io/badge/Stability-99%25-success
|
|
313
|
+
[AUTOPASSWORD_EFF]: https://img.shields.io/badge/Stability-90%25-success
|
|
314
|
+
[AUTOTRANSLATE_EFF]: https://img.shields.io/badge/Stability-25%25-red
|
|
315
|
+
[AUTOSPELL_EFF]: https://img.shields.io/badge/Stability-25%25-red
|
|
316
|
+
[AUTODOWNLOAD_EFF]: https://img.shields.io/badge/Stability-75%25-yellow
|
|
317
|
+
[AUTOIP_EFF]: https://img.shields.io/badge/Stability-95%25-success
|
|
@@ -8,7 +8,7 @@ autotools/autocaps/tests/test_autocaps_core.py,sha256=fzpci_sK7L1fSf_IJ1paJ__bmn
|
|
|
8
8
|
autotools/autocaps/tests/test_autocaps_integration.py,sha256=Qe2hzVEvzf0-INp14oTTrHi0RiRDCE2fxo9abVNcd_E,1435
|
|
9
9
|
autotools/autodownload/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
10
|
autotools/autodownload/commands.py,sha256=WBG4o5716zQ63t23k-qJLNv41L58RnL2XTvNl_TsK-w,1442
|
|
11
|
-
autotools/autodownload/core.py,sha256=
|
|
11
|
+
autotools/autodownload/core.py,sha256=h_PoNmGjmKS8owe8rR0Lkm5FsSMPKB9AQ6RjMLBEqtc,16919
|
|
12
12
|
autotools/autoip/__init__.py,sha256=T_5hz9G4reFPXDucdzRoMFPYlAKwTPt9TejOpkRPgn0,23
|
|
13
13
|
autotools/autoip/commands.py,sha256=c4s22yb7aharRAwOmD37Kgbj6rm05XNBtCVyxU7rc7M,1425
|
|
14
14
|
autotools/autoip/core.py,sha256=Q3dzLstZQruwYkSbCSlKQNmKVGCyNpo1DGst5VFtHLU,10123
|
|
@@ -31,14 +31,14 @@ autotools/autotranslate/__init__.py,sha256=6BxuZqhyQhfsZ5x7DkB1BAEpC08GT_5l5bl0A
|
|
|
31
31
|
autotools/autotranslate/commands.py,sha256=6M1D27mrk41uWTLgwGeO-HTUtq0iiHFrptIb1BM7A-s,1794
|
|
32
32
|
autotools/autotranslate/core.py,sha256=H2f90IWr_jNGiJD3XAv-20i5sRGM-VDYWrYt65EDEkI,1836
|
|
33
33
|
autotools/test/__init__.py,sha256=_xWAfk2kKSboEOW9fVUlmNfVHAx-qGYWFb8dxCz8g1w,48
|
|
34
|
-
autotools/test/commands.py,sha256=
|
|
34
|
+
autotools/test/commands.py,sha256=0IMW_tnbVuyVIOOhWwjJ7SclD8l0m0JobTNc6QHx0-s,4643
|
|
35
35
|
autotools/utils/__init__.py,sha256=2uAirI6ZbOwSFPSg5wuEjA0gMWf1XBJ4yP_WcGeND7M,183
|
|
36
36
|
autotools/utils/loading.py,sha256=cRh8rvNLT4B2aVgWq7flg8bNpnp1XlNBV3_cvxjfm8E,439
|
|
37
37
|
autotools/utils/updates.py,sha256=ZGS8pAVxvy5ORuey_zrBWdA0_hKNmEcwzJ-6c22x4Yo,1140
|
|
38
38
|
autotools/utils/version.py,sha256=sHwkZ8MNTKQdTntV1B-8QInbMnMtR7Xr3DAY5-wf5-0,3027
|
|
39
|
-
Open_AutoTools-0.0.
|
|
40
|
-
Open_AutoTools-0.0.
|
|
41
|
-
Open_AutoTools-0.0.
|
|
42
|
-
Open_AutoTools-0.0.
|
|
43
|
-
Open_AutoTools-0.0.
|
|
44
|
-
Open_AutoTools-0.0.
|
|
39
|
+
Open_AutoTools-0.0.3rc5.dist-info/LICENSE,sha256=SpbSRxNWos2l0-geleCa6d0L9G_bOsZRkY4rB9OduJ0,1069
|
|
40
|
+
Open_AutoTools-0.0.3rc5.dist-info/METADATA,sha256=J92dDLDCnxrBMvTwP6XstY3ps97jSK_QlD0bQOlQLYk,10506
|
|
41
|
+
Open_AutoTools-0.0.3rc5.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
42
|
+
Open_AutoTools-0.0.3rc5.dist-info/entry_points.txt,sha256=QLIsUk6vHo0wAYDk1K74kIuYunJMwWe2xbwQhJXLoKo,312
|
|
43
|
+
Open_AutoTools-0.0.3rc5.dist-info/top_level.txt,sha256=x5ZRvdQw7DQnVmR0YDqVSAuuS94KTHDmk6uIeW7YOPw,10
|
|
44
|
+
Open_AutoTools-0.0.3rc5.dist-info/RECORD,,
|
autotools/autodownload/core.py
CHANGED
|
@@ -129,10 +129,26 @@ def save_consent_status(status):
|
|
|
129
129
|
# IF SAVING FAILS, RETURN FALSE TO FORCE NEW CONSENT NEXT TIME
|
|
130
130
|
return False
|
|
131
131
|
|
|
132
|
+
# FUNCTION TO SAFELY PRINT WITH EMOJI FALLBACK
|
|
133
|
+
def safe_print(text):
|
|
134
|
+
"""PRINT TEXT WITH EMOJI FALLBACK FOR WINDOWS"""
|
|
135
|
+
try:
|
|
136
|
+
print(text)
|
|
137
|
+
except UnicodeEncodeError:
|
|
138
|
+
# REPLACE EMOJIS WITH ASCII ALTERNATIVES
|
|
139
|
+
text = (text.replace('⚠️', '!')
|
|
140
|
+
.replace('🔍', '*')
|
|
141
|
+
.replace('🎥', '>')
|
|
142
|
+
.replace('📋', '+')
|
|
143
|
+
.replace('❌', 'X')
|
|
144
|
+
.replace('✅', 'V')
|
|
145
|
+
.replace('↓', 'v'))
|
|
146
|
+
print(text)
|
|
147
|
+
|
|
132
148
|
# FUNCTION TO GET USER CONSENT WITH INTERACTIVE PROMPT
|
|
133
149
|
def get_user_consent():
|
|
134
150
|
"""GET USER CONSENT WITH INTERACTIVE PROMPT"""
|
|
135
|
-
|
|
151
|
+
safe_print("\n! Important Notice:")
|
|
136
152
|
print("This tool will:")
|
|
137
153
|
print("1. Download video content from YouTube")
|
|
138
154
|
print("2. Save files to your local machine")
|
|
@@ -178,12 +194,12 @@ def download_youtube_video(url, format='mp4', quality='best'):
|
|
|
178
194
|
"""DOWNLOAD VIDEO WITH CONSENT CHECK"""
|
|
179
195
|
# VALIDATE URL FIRST
|
|
180
196
|
if not validate_youtube_url(url):
|
|
181
|
-
|
|
197
|
+
safe_print("\nX Invalid YouTube URL")
|
|
182
198
|
return False
|
|
183
199
|
|
|
184
200
|
# CHECK FOR SAVED CONSENT FIRST AND GET NEW CONSENT IF NEEDED
|
|
185
201
|
if not load_consent_status() and not get_user_consent():
|
|
186
|
-
|
|
202
|
+
safe_print("\nX Download cancelled by user")
|
|
187
203
|
return False
|
|
188
204
|
|
|
189
205
|
# FIRST CHECK VIDEO INFO AND EXISTENCE
|
|
@@ -192,20 +208,53 @@ def download_youtube_video(url, format='mp4', quality='best'):
|
|
|
192
208
|
'quiet': True,
|
|
193
209
|
'no_warnings': True,
|
|
194
210
|
'extractor_args': {'youtube': {
|
|
195
|
-
'player_client': ['android'],
|
|
211
|
+
'player_client': ['web', 'android'],
|
|
196
212
|
'formats': ['missing_pot'] # ALLOW FORMATS WITHOUT PO TOKEN
|
|
197
213
|
}}
|
|
198
214
|
}) as ydl:
|
|
199
215
|
info = ydl.extract_info(url, download=False)
|
|
200
216
|
formats = info.get('formats', [])
|
|
201
217
|
if not formats:
|
|
202
|
-
|
|
218
|
+
safe_print("\nX No formats available for this video")
|
|
203
219
|
return False
|
|
204
220
|
|
|
221
|
+
# FIND BEST AVAILABLE QUALITY
|
|
222
|
+
best_height = 0
|
|
223
|
+
for f in formats:
|
|
224
|
+
height = f.get('height')
|
|
225
|
+
if height is not None and height > best_height:
|
|
226
|
+
best_height = height
|
|
227
|
+
|
|
228
|
+
# IF NO VALID HEIGHT FOUND, DEFAULT TO 1080P
|
|
229
|
+
if best_height == 0:
|
|
230
|
+
best_height = 1080
|
|
231
|
+
|
|
232
|
+
# IF QUALITY IS 'BEST', USE THE BEST AVAILABLE
|
|
233
|
+
if quality == 'best':
|
|
234
|
+
height = best_height
|
|
235
|
+
# ASK FOR CONFIRMATION IF 4K OR HIGHER (ONLY FOR MP4)
|
|
236
|
+
if format == 'mp4' and height >= 2160:
|
|
237
|
+
safe_print(f"\n! This video is available in {height}p quality!")
|
|
238
|
+
while True:
|
|
239
|
+
response = input(f"Do you want to download in {height}p quality? (yes/no): ").lower()
|
|
240
|
+
if response in ['no', 'n']:
|
|
241
|
+
height = 1080
|
|
242
|
+
print("\nDowngrading to 1080p quality.")
|
|
243
|
+
break
|
|
244
|
+
elif response in ['yes', 'y']:
|
|
245
|
+
break
|
|
246
|
+
print("Please answer 'yes' or 'no'")
|
|
247
|
+
else:
|
|
248
|
+
# EXTRACT HEIGHT FROM QUALITY STRING
|
|
249
|
+
try:
|
|
250
|
+
height = int(quality.lower().replace('p', ''))
|
|
251
|
+
except ValueError:
|
|
252
|
+
height = 1080 # DEFAULT TO 1080P IF INVALID FORMAT
|
|
253
|
+
|
|
205
254
|
# CHECK IF FILE EXISTS AND GET REPLACEMENT CONSENT
|
|
206
255
|
force_download = check_existing_video(info, format)
|
|
207
256
|
if not force_download:
|
|
208
|
-
|
|
257
|
+
safe_print("\nX Download cancelled - file already exists")
|
|
209
258
|
return False
|
|
210
259
|
|
|
211
260
|
# OPEN DOWNLOADS FOLDER IF STARTING NEW DOWNLOAD OR REPLACING
|
|
@@ -213,7 +262,7 @@ def download_youtube_video(url, format='mp4', quality='best'):
|
|
|
213
262
|
open_download_folder(download_dir)
|
|
214
263
|
|
|
215
264
|
except Exception as e:
|
|
216
|
-
|
|
265
|
+
safe_print(f"\nX Error checking video: {str(e)}")
|
|
217
266
|
return False
|
|
218
267
|
|
|
219
268
|
loading = LoadingAnimation()
|
|
@@ -221,37 +270,83 @@ def download_youtube_video(url, format='mp4', quality='best'):
|
|
|
221
270
|
# START LOADING FOR DOWNLOAD PROCESS
|
|
222
271
|
with loading:
|
|
223
272
|
loading._spinner.start()
|
|
224
|
-
|
|
273
|
+
safe_print("\n* Starting download...")
|
|
225
274
|
|
|
226
|
-
|
|
227
|
-
|
|
275
|
+
safe_print(f"\n> Downloading video from: {url}")
|
|
276
|
+
if format == 'mp3':
|
|
277
|
+
safe_print(f"+ Format: {format}\n")
|
|
278
|
+
else:
|
|
279
|
+
safe_print(f"+ Format: {format}, Quality: {height}p\n")
|
|
228
280
|
|
|
229
281
|
# YT-DLP PERMISSION OPTIONS FOR DOWNLOADING YOUTUBE VIDEOS
|
|
230
282
|
ydl_opts = {
|
|
231
|
-
'format':
|
|
283
|
+
'format': (
|
|
284
|
+
f'bestvideo[height<={height}][ext=mp4]+bestaudio[ext=m4a]/' # TRY EXACT HEIGHT MATCH FIRST
|
|
285
|
+
f'bestvideo[height<={height}][ext=webm]+bestaudio[ext=webm]/' # TRY WEBM AS FALLBACK
|
|
286
|
+
f'best[height<={height}]/' # TRY COMBINED FORMATS
|
|
287
|
+
'best' # FALLBACK TO BEST AVAILABLE
|
|
288
|
+
) if format == 'mp4' else 'bestaudio/best',
|
|
289
|
+
'postprocessors': [{
|
|
290
|
+
'key': 'FFmpegExtractAudio',
|
|
291
|
+
'preferredcodec': 'mp3',
|
|
292
|
+
'preferredquality': '192',
|
|
293
|
+
}] if format == 'mp3' else [],
|
|
232
294
|
'quiet': True,
|
|
233
295
|
'no_warnings': True,
|
|
234
296
|
'progress': True,
|
|
235
|
-
'progress_hooks': [lambda d:
|
|
297
|
+
'progress_hooks': [lambda d: update_progress(d)],
|
|
236
298
|
'extractor_args': {
|
|
237
299
|
'youtube': {
|
|
238
|
-
'player_client': ['android'],
|
|
239
|
-
'formats': ['missing_pot'] # ALLOW FORMATS WITHOUT PO TOKEN
|
|
300
|
+
'player_client': ['android', 'web'], # USE ANDROID FIRST AND WEB PLAYER CLIENTS IF ANDROID FAILS
|
|
301
|
+
'formats': ['missing_pot'], # ALLOW FORMATS WITHOUT PO TOKEN
|
|
302
|
+
'player_skip': ['configs', 'webpage'] # SKIP UNNECESSARY CONFIGS
|
|
240
303
|
}
|
|
241
304
|
},
|
|
242
305
|
'http_headers': {
|
|
243
|
-
'User-Agent': 'Mozilla/5.0 (Linux; Android 12
|
|
306
|
+
'User-Agent': 'Mozilla/5.0 (Linux; Android 12) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36',
|
|
307
|
+
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
|
308
|
+
'Accept-Language': 'en-us,en;q=0.5',
|
|
309
|
+
'Sec-Fetch-Mode': 'navigate'
|
|
244
310
|
},
|
|
245
311
|
'outtmpl': str(download_dir / '%(title)s.%(ext)s'), # SET OUTPUT TEMPLATE
|
|
246
|
-
'overwrites': True # FORCE OVERWRITE IF USER CONSENTED
|
|
312
|
+
'overwrites': True, # FORCE OVERWRITE IF USER CONSENTED
|
|
313
|
+
'no_check_certificates': True, # SKIP CERTIFICATE VALIDATION
|
|
314
|
+
'ignoreerrors': False, # CATCH ERRORS PROPERLY
|
|
315
|
+
'cookiesfrombrowser': None, # DISABLE COOKIE FILE
|
|
316
|
+
'cookiefile': None, # DISABLE COOKIE FILE
|
|
247
317
|
}
|
|
248
318
|
|
|
249
319
|
try:
|
|
250
320
|
# THEN DOWNLOAD
|
|
251
321
|
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
322
|
+
try:
|
|
323
|
+
ydl.download([url])
|
|
324
|
+
safe_print("\nV Download completed successfully!")
|
|
325
|
+
return True
|
|
326
|
+
except Exception as e:
|
|
327
|
+
if "HTTP Error 403" in str(e):
|
|
328
|
+
safe_print("\n! Access denied for requested quality. Trying lower quality...")
|
|
329
|
+
# TRY DOWNLOADING WITH LOWER QUALITY
|
|
330
|
+
if height > 720:
|
|
331
|
+
new_height = min(height - 360, 1080) # STEP DOWN QUALITY
|
|
332
|
+
safe_print(f"v Falling back to {new_height}p")
|
|
333
|
+
ydl_opts['format'] = (
|
|
334
|
+
f'bestvideo[height<={new_height}][ext=mp4]+bestaudio[ext=m4a]/'
|
|
335
|
+
f'bestvideo[height<={new_height}][ext=webm]+bestaudio[ext=webm]/'
|
|
336
|
+
f'best[height<={new_height}]/'
|
|
337
|
+
'best'
|
|
338
|
+
)
|
|
339
|
+
with yt_dlp.YoutubeDL(ydl_opts) as ydl2:
|
|
340
|
+
ydl2.download([url])
|
|
341
|
+
safe_print("\nV Download completed successfully!")
|
|
342
|
+
return True
|
|
343
|
+
else:
|
|
344
|
+
safe_print("\nX Failed to download video at any quality")
|
|
345
|
+
return False
|
|
346
|
+
else:
|
|
347
|
+
raise e
|
|
348
|
+
|
|
349
|
+
# CATCH ANY EXCEPTIONS AND HANDLE THEM
|
|
255
350
|
except Exception as e:
|
|
256
351
|
error_msg = str(e)
|
|
257
352
|
if "Requested format is not available" in error_msg:
|
|
@@ -281,23 +376,22 @@ pbar = None # GLOBAL VARIABLE TO STORE PROGRESS BAR
|
|
|
281
376
|
|
|
282
377
|
|
|
283
378
|
# FUNCTION TO UPDATE PROGRESS BAR
|
|
284
|
-
def
|
|
379
|
+
def update_progress(d):
|
|
285
380
|
global pbar
|
|
286
|
-
|
|
287
381
|
if d['status'] == 'downloading':
|
|
288
382
|
total = d.get('total_bytes', 0)
|
|
289
383
|
downloaded = d.get('downloaded_bytes', 0)
|
|
290
384
|
|
|
291
385
|
if pbar is None:
|
|
292
|
-
pbar = tqdm(total=total, unit='B', unit_scale=True, desc="
|
|
386
|
+
pbar = tqdm(total=total, unit='B', unit_scale=True, desc="⏳ Downloading", leave=True, ncols=80, bar_format='{desc}: {percentage:3.0f}%|{bar}| {n_fmt}/{total_fmt} [{rate_fmt}]')
|
|
293
387
|
|
|
294
|
-
|
|
295
|
-
|
|
388
|
+
if total > 0:
|
|
389
|
+
pbar.n = downloaded
|
|
390
|
+
pbar.total = total
|
|
391
|
+
pbar.refresh()
|
|
296
392
|
|
|
297
393
|
elif d['status'] == 'finished' and pbar:
|
|
298
|
-
pbar.n = pbar.total
|
|
299
394
|
pbar.close()
|
|
300
|
-
print("Download completed")
|
|
301
395
|
pbar = None
|
|
302
396
|
|
|
303
397
|
|
|
@@ -325,3 +419,15 @@ def download_file_with_tqdm(url):
|
|
|
325
419
|
open_download_folder(download_dir)
|
|
326
420
|
except requests.exceptions.RequestException as e:
|
|
327
421
|
print(f"Error during file download: {e}")
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
# FUNCTION TO GET BROWSER COOKIES
|
|
425
|
+
def get_browser_cookies():
|
|
426
|
+
"""GET BROWSER COOKIES WITH FALLBACK OPTIONS"""
|
|
427
|
+
try:
|
|
428
|
+
return ('chrome',) # TRY CHROME FIRST
|
|
429
|
+
except Exception:
|
|
430
|
+
try:
|
|
431
|
+
return ('firefox',) # TRY FIREFOX IF CHROME FAILS
|
|
432
|
+
except Exception:
|
|
433
|
+
return None # RETURN NONE IF BOTH FAIL
|
autotools/test/commands.py
CHANGED
|
@@ -28,34 +28,32 @@ def test(unit, integration, no_cov, html, module):
|
|
|
28
28
|
|
|
29
29
|
# BASE COMMAND WITH ENHANCED VERBOSITY
|
|
30
30
|
cmd = [
|
|
31
|
-
|
|
32
|
-
'
|
|
33
|
-
'
|
|
34
|
-
'
|
|
35
|
-
'--
|
|
36
|
-
'--
|
|
37
|
-
'
|
|
38
|
-
'--tb=long', # LONG TRACEBACK STYLE
|
|
39
|
-
'-s' # SHORTCUT FOR --capture=no
|
|
31
|
+
sys.executable, # USE SYSTEM PYTHON EXECUTABLE
|
|
32
|
+
'-m', 'pytest', # RUN PYTEST AS MODULE
|
|
33
|
+
'-vv', # VERY VERBOSE OUTPUT
|
|
34
|
+
'--capture=no', # SHOW PRINT STATEMENTS
|
|
35
|
+
'--showlocals', # SHOW LOCAL VARIABLES IN TRACEBACKS
|
|
36
|
+
'--log-cli-level=DEBUG', # SHOW DEBUG LOGS
|
|
37
|
+
'-s', # DISABLE CAPTURE
|
|
40
38
|
]
|
|
41
39
|
|
|
42
40
|
# COVERAGE OPTIONS
|
|
43
41
|
if not no_cov:
|
|
44
|
-
cmd.extend(['--cov=autotools'])
|
|
45
42
|
if html:
|
|
46
|
-
cmd.extend(['--cov-report=html'])
|
|
43
|
+
cmd.extend(['--cov-report=html', '--cov=autotools'])
|
|
47
44
|
else:
|
|
48
|
-
cmd.extend(['--cov-report=term-missing'])
|
|
45
|
+
cmd.extend(['--cov-report=term-missing', '--cov=autotools'])
|
|
49
46
|
|
|
50
47
|
# TEST SELECTION
|
|
51
|
-
test_path = 'autotools'
|
|
52
48
|
if module:
|
|
49
|
+
test_path = f'autotools/{module}/tests'
|
|
53
50
|
if unit and not integration:
|
|
54
|
-
|
|
51
|
+
test_path = f'{test_path}/unit'
|
|
55
52
|
elif integration and not unit:
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
test_path = f'{test_path}/integration'
|
|
54
|
+
cmd.append(test_path)
|
|
55
|
+
else:
|
|
56
|
+
cmd.append('autotools')
|
|
59
57
|
|
|
60
58
|
# SHOW COMMAND BEING RUN
|
|
61
59
|
click.echo(click.style("\nRunning tests with command:", fg='blue', bold=True))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|