cinnamon-cli 0.2.29__tar.gz → 0.2.31__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.
- {cinnamon_cli-0.2.29/cinnamon_cli.egg-info → cinnamon_cli-0.2.31}/PKG-INFO +1 -1
- cinnamon_cli-0.2.31/cinnamon/__init__.py +1 -0
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/cinnamon/player.py +27 -21
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/cinnamon/scrapers/webstream.py +22 -14
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31/cinnamon_cli.egg-info}/PKG-INFO +1 -1
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/pyproject.toml +1 -1
- cinnamon_cli-0.2.29/cinnamon/__init__.py +0 -1
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/LICENSE +0 -0
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/README.md +0 -0
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/cinnamon/anilist.py +0 -0
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/cinnamon/cli.py +0 -0
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/cinnamon/config.py +0 -0
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/cinnamon/downloads.py +0 -0
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/cinnamon/errors.py +0 -0
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/cinnamon/history.py +0 -0
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/cinnamon/scrapers/__init__.py +0 -0
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/cinnamon/scrapers/anime.py +0 -0
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/cinnamon/scrapers/base.py +0 -0
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/cinnamon/scrapers/torrentio.py +0 -0
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/cinnamon/scrapers/vidsrc.py +0 -0
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/cinnamon/tmdb.py +0 -0
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/cinnamon/tui.py +0 -0
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/cinnamon_cli.egg-info/SOURCES.txt +0 -0
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/cinnamon_cli.egg-info/dependency_links.txt +0 -0
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/cinnamon_cli.egg-info/entry_points.txt +0 -0
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/cinnamon_cli.egg-info/requires.txt +0 -0
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/cinnamon_cli.egg-info/top_level.txt +0 -0
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/setup.cfg +0 -0
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/tests/test_directstream.py +0 -0
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/tests/test_directstream2.py +0 -0
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/tests/test_directstream3.py +0 -0
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/tests/test_directstream4.py +0 -0
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/tests/test_directstream5.py +0 -0
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/tests/test_directstream6.py +0 -0
- {cinnamon_cli-0.2.29 → cinnamon_cli-0.2.31}/tests/test_ui_smoke.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.31"
|
|
@@ -489,28 +489,31 @@ def download_video(url, title="", referer=None, output_dir=".", track_id=None):
|
|
|
489
489
|
|
|
490
490
|
import re
|
|
491
491
|
last_pct = -1
|
|
492
|
+
last_error = ""
|
|
492
493
|
try:
|
|
493
494
|
for line in proc.stdout:
|
|
494
|
-
if "[download]"
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
m
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
495
|
+
if "[download]" in line:
|
|
496
|
+
m = re.search(r"(\d+\.?\d*)%\s+of\s+~?\s*([\d.]+)(\w+).*ETA\s+(\S+)", line)
|
|
497
|
+
if not m:
|
|
498
|
+
m = re.search(r"(\d+\.?\d*)%\s+of\s+~?\s*([\d.]+)(\w+)", line)
|
|
499
|
+
if m:
|
|
500
|
+
pct = float(m.group(1))
|
|
501
|
+
if int(pct) == last_pct:
|
|
502
|
+
continue
|
|
503
|
+
last_pct = int(pct)
|
|
504
|
+
size_val = float(m.group(2))
|
|
505
|
+
unit = m.group(3)
|
|
506
|
+
eta = m.group(4) if m.lastindex >= 4 else ""
|
|
507
|
+
bar_width = 25
|
|
508
|
+
filled = int(bar_width * pct / 100)
|
|
509
|
+
bar = "█" * filled + "░" * (bar_width - filled)
|
|
510
|
+
eta_str = f" ETA {eta}" if eta else ""
|
|
511
|
+
print(f"\r {bar} {pct:>5.1f}% {size_val:.1f}{unit}{eta_str} ", end="", file=sys.stderr, flush=True)
|
|
512
|
+
else:
|
|
513
|
+
print(f"\r {line.rstrip():<50}", file=sys.stderr, flush=True)
|
|
514
|
+
elif "ERROR:" in line or "WARNING:" in line:
|
|
515
|
+
last_error = line.strip()
|
|
516
|
+
print(f" {line.rstrip()}", file=sys.stderr, flush=True)
|
|
514
517
|
|
|
515
518
|
proc.wait()
|
|
516
519
|
print(file=sys.stderr)
|
|
@@ -520,7 +523,10 @@ def download_video(url, title="", referer=None, output_dir=".", track_id=None):
|
|
|
520
523
|
_track_update(track_id, status="completed")
|
|
521
524
|
print(f" Done — {safe}", file=sys.stderr)
|
|
522
525
|
else:
|
|
523
|
-
|
|
526
|
+
detail = last_error or f"exit code {proc.returncode}"
|
|
527
|
+
if track_id:
|
|
528
|
+
_track_update(track_id, status="error")
|
|
529
|
+
raise PlayerLaunchError("yt-dlp", detail)
|
|
524
530
|
|
|
525
531
|
except KeyboardInterrupt:
|
|
526
532
|
print(file=sys.stderr)
|
|
@@ -75,7 +75,7 @@ class WebStreamScraper(BaseScraper):
|
|
|
75
75
|
|
|
76
76
|
if _time.time() < deadline:
|
|
77
77
|
try:
|
|
78
|
-
result = vidlink_fn(tmdb_id)
|
|
78
|
+
result = vidlink_fn(tmdb_id, quality)
|
|
79
79
|
if result:
|
|
80
80
|
label = quality.upper() if quality else "Auto"
|
|
81
81
|
return ScraperResult(
|
|
@@ -113,7 +113,7 @@ class WebStreamScraper(BaseScraper):
|
|
|
113
113
|
|
|
114
114
|
if _time.time() < deadline:
|
|
115
115
|
try:
|
|
116
|
-
result = vidlink_fn(tmdb_id, season, episode)
|
|
116
|
+
result = vidlink_fn(tmdb_id, season, episode, quality)
|
|
117
117
|
if result:
|
|
118
118
|
label = quality.upper() if quality else "Auto"
|
|
119
119
|
return ScraperResult(
|
|
@@ -194,7 +194,23 @@ def _try_vixsrc(tmdb_id, season, episode, quality="") -> Optional[str]:
|
|
|
194
194
|
return match[1]
|
|
195
195
|
|
|
196
196
|
|
|
197
|
-
def
|
|
197
|
+
def _vidlink_quality_url(data, quality=""):
|
|
198
|
+
"""vidlink returns direct mp4 URLs per resolution under stream.qualities."""
|
|
199
|
+
qualities = data.get("stream", {}).get("qualities", {})
|
|
200
|
+
if not qualities:
|
|
201
|
+
raise ScraperNetworkError("vidlink", "No qualities in stream response")
|
|
202
|
+
if quality and quality not in ("best", "worst"):
|
|
203
|
+
target = quality.replace("p", "")
|
|
204
|
+
if target in qualities:
|
|
205
|
+
return qualities[target]["url"]
|
|
206
|
+
if quality == "worst":
|
|
207
|
+
res = min(qualities, key=lambda r: int(r))
|
|
208
|
+
else:
|
|
209
|
+
res = max(qualities, key=lambda r: int(r))
|
|
210
|
+
return qualities[res]["url"]
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _try_vidlink(tmdb_id, season, episode, quality="") -> Optional[str]:
|
|
198
214
|
session = requests.Session()
|
|
199
215
|
session.headers.update({"User-Agent": UA})
|
|
200
216
|
|
|
@@ -222,11 +238,7 @@ def _try_vidlink(tmdb_id, season, episode) -> Optional[str]:
|
|
|
222
238
|
data = stream_resp.json()
|
|
223
239
|
except ValueError:
|
|
224
240
|
raise ScraperNetworkError("vidlink", "Non-JSON response from stream API")
|
|
225
|
-
|
|
226
|
-
if not playlist:
|
|
227
|
-
raise ScraperNetworkError("vidlink", "No playlist in stream response")
|
|
228
|
-
|
|
229
|
-
return playlist
|
|
241
|
+
return _vidlink_quality_url(data, quality)
|
|
230
242
|
|
|
231
243
|
|
|
232
244
|
def _try_vixsrc_movie(tmdb_id, quality="") -> Optional[str]:
|
|
@@ -291,7 +303,7 @@ def _try_vixsrc_movie(tmdb_id, quality="") -> Optional[str]:
|
|
|
291
303
|
return match[1]
|
|
292
304
|
|
|
293
305
|
|
|
294
|
-
def _try_vidlink_movie(tmdb_id) -> Optional[str]:
|
|
306
|
+
def _try_vidlink_movie(tmdb_id, quality="") -> Optional[str]:
|
|
295
307
|
session = requests.Session()
|
|
296
308
|
session.headers.update({"User-Agent": UA})
|
|
297
309
|
|
|
@@ -319,8 +331,4 @@ def _try_vidlink_movie(tmdb_id) -> Optional[str]:
|
|
|
319
331
|
data = stream_resp.json()
|
|
320
332
|
except ValueError:
|
|
321
333
|
raise ScraperNetworkError("vidlink", "Non-JSON response from stream API")
|
|
322
|
-
|
|
323
|
-
if not playlist:
|
|
324
|
-
raise ScraperNetworkError("vidlink", "No playlist in stream response")
|
|
325
|
-
|
|
326
|
-
return playlist
|
|
334
|
+
return _vidlink_quality_url(data, quality)
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "cinnamon-cli"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.31"
|
|
8
8
|
description = "CLI to search TV shows, movies & anime via TMDB/AniList, scrape m3u8 links, and play in VLC/mpv"
|
|
9
9
|
license = { text = "CC BY-NC 4.0" }
|
|
10
10
|
readme = "README.md"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.2.29"
|
|
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
|