polytool 0.2.6__tar.gz → 0.2.8__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.
- {polytool-0.2.6 → polytool-0.2.8}/PKG-INFO +3 -1
- {polytool-0.2.6 → polytool-0.2.8}/pyproject.toml +2 -2
- {polytool-0.2.6 → polytool-0.2.8}/src/polytool/__init__.py +1 -1
- {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/dl.py +51 -7
- {polytool-0.2.6 → polytool-0.2.8}/.gitignore +0 -0
- {polytool-0.2.6 → polytool-0.2.8}/LICENSE +0 -0
- {polytool-0.2.6 → polytool-0.2.8}/README.md +0 -0
- {polytool-0.2.6 → polytool-0.2.8}/docs/README.md +0 -0
- {polytool-0.2.6 → polytool-0.2.8}/src/polytool/__main__.py +0 -0
- {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/__init__.py +0 -0
- {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/clip.py +0 -0
- {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/color.py +0 -0
- {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/convert.py +0 -0
- {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/cron.py +0 -0
- {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/data.py +0 -0
- {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/enc.py +0 -0
- {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/file.py +0 -0
- {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/gen.py +0 -0
- {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/img.py +0 -0
- {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/net.py +0 -0
- {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/pdf.py +0 -0
- {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/qr.py +0 -0
- {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/shot.py +0 -0
- {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/text.py +0 -0
- {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/vid.py +0 -0
- {polytool-0.2.6 → polytool-0.2.8}/src/polytool/core/__init__.py +0 -0
- {polytool-0.2.6 → polytool-0.2.8}/src/polytool/core/browsers.py +0 -0
- {polytool-0.2.6 → polytool-0.2.8}/src/polytool/core/config.py +0 -0
- {polytool-0.2.6 → polytool-0.2.8}/src/polytool/core/console.py +0 -0
- {polytool-0.2.6 → polytool-0.2.8}/src/polytool/core/errors.py +0 -0
- {polytool-0.2.6 → polytool-0.2.8}/src/polytool/core/ffmpeg.py +0 -0
- {polytool-0.2.6 → polytool-0.2.8}/src/polytool/core/io.py +0 -0
- {polytool-0.2.6 → polytool-0.2.8}/src/polytool/core/lazy.py +0 -0
- {polytool-0.2.6 → polytool-0.2.8}/src/polytool/core/progress.py +0 -0
- {polytool-0.2.6 → polytool-0.2.8}/src/polytool/core/runtime.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: polytool
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.8
|
|
4
4
|
Summary: One-binary CLI bundling 26 everyday utilities — image/video/PDF conversion, background removal, OCR, QR codes, hashing, downloads, and more
|
|
5
5
|
Project-URL: Homepage, https://github.com/k6w/polytool
|
|
6
6
|
Project-URL: Repository, https://github.com/k6w/polytool
|
|
@@ -48,6 +48,7 @@ Requires-Dist: pytest-cov>=5; extra == 'dev'
|
|
|
48
48
|
Requires-Dist: pytest>=8; extra == 'dev'
|
|
49
49
|
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
50
50
|
Provides-Extra: dl
|
|
51
|
+
Requires-Dist: yt-dlp-ejs>=0.8; extra == 'dl'
|
|
51
52
|
Requires-Dist: yt-dlp>=2024.5; extra == 'dl'
|
|
52
53
|
Provides-Extra: full
|
|
53
54
|
Requires-Dist: ascii-magic>=2; extra == 'full'
|
|
@@ -70,6 +71,7 @@ Requires-Dist: pytesseract>=0.3; extra == 'full'
|
|
|
70
71
|
Requires-Dist: pyzbar>=0.1; extra == 'full'
|
|
71
72
|
Requires-Dist: rembg[cpu]>=2.0; extra == 'full'
|
|
72
73
|
Requires-Dist: resvg-py>=0.1; extra == 'full'
|
|
74
|
+
Requires-Dist: yt-dlp-ejs>=0.8; extra == 'full'
|
|
73
75
|
Requires-Dist: yt-dlp>=2024.5; extra == 'full'
|
|
74
76
|
Provides-Extra: img
|
|
75
77
|
Requires-Dist: ascii-magic>=2; extra == 'img'
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "polytool"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.8"
|
|
8
8
|
description = "One-binary CLI bundling 26 everyday utilities — image/video/PDF conversion, background removal, OCR, QR codes, hashing, downloads, and more"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.13"
|
|
@@ -58,7 +58,7 @@ img = [
|
|
|
58
58
|
]
|
|
59
59
|
vid = ["ffmpeg-python>=0.2", "imageio-ffmpeg>=0.5"]
|
|
60
60
|
pdf = ["pypdf>=4", "pikepdf>=8", "pdfplumber>=0.11", "pymupdf>=1.24"]
|
|
61
|
-
dl = ["yt-dlp>=2024.5"]
|
|
61
|
+
dl = ["yt-dlp>=2024.5", "yt-dlp-ejs>=0.8"]
|
|
62
62
|
ocr = ["pytesseract>=0.3", "easyocr>=1.7"]
|
|
63
63
|
ai = ["rembg[cpu]>=2.0"]
|
|
64
64
|
qr-decode = ["pyzbar>=0.1"]
|
|
@@ -56,6 +56,13 @@ N_CHALLENGE_PATTERNS = (
|
|
|
56
56
|
"requested format is not available",
|
|
57
57
|
)
|
|
58
58
|
|
|
59
|
+
# Patterns that indicate per-session DRM lock — not something we can solve.
|
|
60
|
+
DRM_PATTERNS = (
|
|
61
|
+
"drm protected",
|
|
62
|
+
"drm-protected",
|
|
63
|
+
"experiment that applies drm",
|
|
64
|
+
)
|
|
65
|
+
|
|
59
66
|
|
|
60
67
|
_ANSI_RE = re.compile(r"\x1b\[[0-9;]*[A-Za-z]")
|
|
61
68
|
_NOISE_RE = re.compile(
|
|
@@ -142,19 +149,56 @@ def _n_challenge_hint(messages: list[str]) -> str | None:
|
|
|
142
149
|
text = " ".join(messages).lower()
|
|
143
150
|
if not any(p in text for p in N_CHALLENGE_PATTERNS):
|
|
144
151
|
return None
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
152
|
+
|
|
153
|
+
# When cookies are configured we know we're in a logged-in session — that's
|
|
154
|
+
# by far the most common cause of these errors (YouTube tightens access for
|
|
155
|
+
# accounts in their DRM A/B bucket). Try anonymous access first.
|
|
156
|
+
using_cookies = bool(
|
|
157
|
+
config.get("dl", "cookies_from_browser") or config.get("dl", "cookies_file")
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
fixes: list[str] = []
|
|
161
|
+
if using_cookies:
|
|
162
|
+
fixes.append(
|
|
163
|
+
"[bold]Most likely fix:[/bold] try without cookies. YouTube often gates "
|
|
164
|
+
"logged-in sessions into a DRM-only bucket.\n"
|
|
165
|
+
" [cyan]pt dl setup --clear[/cyan] [dim]# forget saved cookies (just for testing)[/dim]\n"
|
|
166
|
+
" [cyan]pt dl get URL[/cyan]\n"
|
|
167
|
+
"If that works, re-save cookies for sites that need them: "
|
|
168
|
+
"[cyan]pt dl setup --browser firefox[/cyan]"
|
|
169
|
+
)
|
|
170
|
+
fixes.append(
|
|
171
|
+
"If it still fails, make sure a JS runtime is on PATH. Polytool can manage one:\n"
|
|
148
172
|
" [cyan]pt dl runtime install[/cyan] "
|
|
149
|
-
"[dim]# downloads Deno (~50 MB) into ~/.polytool/runtime/[/dim]
|
|
150
|
-
|
|
173
|
+
"[dim]# downloads Deno (~50 MB) into ~/.polytool/runtime/[/dim]"
|
|
174
|
+
)
|
|
175
|
+
fixes.append(
|
|
176
|
+
"Some videos are flagged DRM in your specific YouTube session — those can't "
|
|
177
|
+
"be downloaded by any tool. Try a different video or account."
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
return "\n\n".join(fixes)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _drm_hint(messages: list[str]) -> str | None:
|
|
184
|
+
text = " ".join(messages).lower()
|
|
185
|
+
if not any(p in text for p in DRM_PATTERNS):
|
|
186
|
+
return None
|
|
187
|
+
return (
|
|
188
|
+
"YouTube has flagged this video as DRM-protected for your current session.\n"
|
|
189
|
+
"This is a per-account / per-session A/B experiment yt-dlp can't bypass.\n\n"
|
|
190
|
+
"Workarounds (any one usually works):\n"
|
|
191
|
+
" • Try without cookies: [cyan]pt dl get URL --cookies-from-browser ''[/cyan]\n"
|
|
192
|
+
" • Use a different browser profile: [cyan]pt dl setup --browser chrome[/cyan]\n"
|
|
193
|
+
" • Try a different YouTube account.\n"
|
|
194
|
+
" • Pick a different video — only some are DRM-flagged in your session."
|
|
151
195
|
)
|
|
152
196
|
|
|
153
197
|
|
|
154
198
|
def _hint_for_error(message: str, logger: _DlLogger) -> str | None:
|
|
155
|
-
"""Pick the best hint for a failure: bot-check
|
|
199
|
+
"""Pick the best hint for a failure: DRM > bot-check > n-challenge."""
|
|
156
200
|
pool = [message, *logger.warnings, *logger.errors]
|
|
157
|
-
return _bot_check_hint(message) or _n_challenge_hint(pool)
|
|
201
|
+
return _drm_hint(pool) or _bot_check_hint(message) or _n_challenge_hint(pool)
|
|
158
202
|
|
|
159
203
|
|
|
160
204
|
# --------------------------------------------------------------------------- #
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|