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.
Files changed (35) hide show
  1. {polytool-0.2.6 → polytool-0.2.8}/PKG-INFO +3 -1
  2. {polytool-0.2.6 → polytool-0.2.8}/pyproject.toml +2 -2
  3. {polytool-0.2.6 → polytool-0.2.8}/src/polytool/__init__.py +1 -1
  4. {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/dl.py +51 -7
  5. {polytool-0.2.6 → polytool-0.2.8}/.gitignore +0 -0
  6. {polytool-0.2.6 → polytool-0.2.8}/LICENSE +0 -0
  7. {polytool-0.2.6 → polytool-0.2.8}/README.md +0 -0
  8. {polytool-0.2.6 → polytool-0.2.8}/docs/README.md +0 -0
  9. {polytool-0.2.6 → polytool-0.2.8}/src/polytool/__main__.py +0 -0
  10. {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/__init__.py +0 -0
  11. {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/clip.py +0 -0
  12. {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/color.py +0 -0
  13. {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/convert.py +0 -0
  14. {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/cron.py +0 -0
  15. {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/data.py +0 -0
  16. {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/enc.py +0 -0
  17. {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/file.py +0 -0
  18. {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/gen.py +0 -0
  19. {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/img.py +0 -0
  20. {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/net.py +0 -0
  21. {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/pdf.py +0 -0
  22. {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/qr.py +0 -0
  23. {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/shot.py +0 -0
  24. {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/text.py +0 -0
  25. {polytool-0.2.6 → polytool-0.2.8}/src/polytool/cli/vid.py +0 -0
  26. {polytool-0.2.6 → polytool-0.2.8}/src/polytool/core/__init__.py +0 -0
  27. {polytool-0.2.6 → polytool-0.2.8}/src/polytool/core/browsers.py +0 -0
  28. {polytool-0.2.6 → polytool-0.2.8}/src/polytool/core/config.py +0 -0
  29. {polytool-0.2.6 → polytool-0.2.8}/src/polytool/core/console.py +0 -0
  30. {polytool-0.2.6 → polytool-0.2.8}/src/polytool/core/errors.py +0 -0
  31. {polytool-0.2.6 → polytool-0.2.8}/src/polytool/core/ffmpeg.py +0 -0
  32. {polytool-0.2.6 → polytool-0.2.8}/src/polytool/core/io.py +0 -0
  33. {polytool-0.2.6 → polytool-0.2.8}/src/polytool/core/lazy.py +0 -0
  34. {polytool-0.2.6 → polytool-0.2.8}/src/polytool/core/progress.py +0 -0
  35. {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.6
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.6"
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"]
@@ -1,4 +1,4 @@
1
1
  """polytool — one-binary CLI bundling 26 everyday utilities."""
2
2
 
3
- __version__ = "0.2.6"
3
+ __version__ = "0.2.8"
4
4
  __all__ = ["__version__"]
@@ -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
- return (
146
- "YouTube needs a JavaScript runtime to solve its n-challenge.\n\n"
147
- "Polytool can manage one for you (no system install needed):\n"
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]\n\n"
150
- "Then re-run [cyan]pt dl get[/cyan]. The runtime is auto-detected on every call."
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 first, then n-challenge."""
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