cinnamon-cli 0.2.28__tar.gz → 0.2.30__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.28/cinnamon_cli.egg-info → cinnamon_cli-0.2.30}/PKG-INFO +3 -3
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/README.md +2 -2
- cinnamon_cli-0.2.30/cinnamon/__init__.py +1 -0
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon/cli.py +2 -0
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon/scrapers/anime.py +1 -1
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon/scrapers/webstream.py +49 -24
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30/cinnamon_cli.egg-info}/PKG-INFO +3 -3
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/pyproject.toml +1 -1
- cinnamon_cli-0.2.28/cinnamon/__init__.py +0 -1
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/LICENSE +0 -0
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon/anilist.py +0 -0
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon/config.py +0 -0
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon/downloads.py +0 -0
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon/errors.py +0 -0
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon/history.py +0 -0
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon/player.py +0 -0
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon/scrapers/__init__.py +0 -0
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon/scrapers/base.py +0 -0
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon/scrapers/torrentio.py +0 -0
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon/scrapers/vidsrc.py +0 -0
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon/tmdb.py +0 -0
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon/tui.py +0 -0
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon_cli.egg-info/SOURCES.txt +0 -0
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon_cli.egg-info/dependency_links.txt +0 -0
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon_cli.egg-info/entry_points.txt +0 -0
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon_cli.egg-info/requires.txt +0 -0
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon_cli.egg-info/top_level.txt +0 -0
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/setup.cfg +0 -0
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/tests/test_directstream.py +0 -0
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/tests/test_directstream2.py +0 -0
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/tests/test_directstream3.py +0 -0
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/tests/test_directstream4.py +0 -0
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/tests/test_directstream5.py +0 -0
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/tests/test_directstream6.py +0 -0
- {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/tests/test_ui_smoke.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cinnamon-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.30
|
|
4
4
|
Summary: CLI to search TV shows, movies & anime via TMDB/AniList, scrape m3u8 links, and play in VLC/mpv
|
|
5
5
|
Author: pizza-droid
|
|
6
6
|
License: CC BY-NC 4.0
|
|
@@ -78,8 +78,8 @@ By default you'll be asked whether to watch sub or dub. Skip the prompt with
|
|
|
78
78
|
`-sub` / `-dub`:
|
|
79
79
|
|
|
80
80
|
```bash
|
|
81
|
-
cinnamon anime "Chainsaw Man" --
|
|
82
|
-
cinnamon anime "Frieren" --
|
|
81
|
+
cinnamon anime "Chainsaw Man" --sub
|
|
82
|
+
cinnamon anime "Frieren" --dub
|
|
83
83
|
```
|
|
84
84
|
|
|
85
85
|
|
|
@@ -49,8 +49,8 @@ By default you'll be asked whether to watch sub or dub. Skip the prompt with
|
|
|
49
49
|
`-sub` / `-dub`:
|
|
50
50
|
|
|
51
51
|
```bash
|
|
52
|
-
cinnamon anime "Chainsaw Man" --
|
|
53
|
-
cinnamon anime "Frieren" --
|
|
52
|
+
cinnamon anime "Chainsaw Man" --sub
|
|
53
|
+
cinnamon anime "Frieren" --dub
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.30"
|
|
@@ -531,6 +531,8 @@ def _resolve_and_play(show, season_num, ep_num, ep_name, scraper, player, qualit
|
|
|
531
531
|
info["quality"] = quality
|
|
532
532
|
if translation:
|
|
533
533
|
info["translation"] = translation
|
|
534
|
+
if download:
|
|
535
|
+
info["download"] = True
|
|
534
536
|
future = pool.submit(attempt.resolve, info)
|
|
535
537
|
result = future.result(timeout=RESOLVE_TIMEOUT)
|
|
536
538
|
if result:
|
|
@@ -352,7 +352,7 @@ class AnimeScraper(BaseScraper):
|
|
|
352
352
|
try:
|
|
353
353
|
direct = _extract_mp4upload(session, url)
|
|
354
354
|
if direct:
|
|
355
|
-
label =
|
|
355
|
+
label = "Dub" if translation == "dub" else "Sub"
|
|
356
356
|
return ScraperResult(
|
|
357
357
|
title=f"{show_name} E{episode:02d} ({label})",
|
|
358
358
|
m3u8_url=direct,
|
|
@@ -42,6 +42,9 @@ class WebStreamScraper(BaseScraper):
|
|
|
42
42
|
season = episode_info.get("season", 1)
|
|
43
43
|
episode = episode_info.get("episode", 1)
|
|
44
44
|
quality = episode_info.get("quality", "")
|
|
45
|
+
# vidlink uses a different CDN that is not throttled like vixsrc's
|
|
46
|
+
# segment edge, so prefer it when downloading to avoid 503 stalls.
|
|
47
|
+
prefer_vidlink = bool(episode_info.get("download"))
|
|
45
48
|
|
|
46
49
|
if not tmdb_id:
|
|
47
50
|
raise ScraperParseError(self.name, "Missing tmdb_id/movie_id/tv_id in episode_info")
|
|
@@ -49,28 +52,34 @@ class WebStreamScraper(BaseScraper):
|
|
|
49
52
|
deadline = _time.time() + 30
|
|
50
53
|
|
|
51
54
|
if media_type == "movie":
|
|
52
|
-
|
|
55
|
+
if prefer_vidlink:
|
|
56
|
+
vixsrc_fn, vidlink_fn = _try_vidlink_movie, _try_vixsrc_movie
|
|
57
|
+
vixsrc_ref = f"https://vixsrc.to/embed/movie/{tmdb_id}"
|
|
58
|
+
else:
|
|
59
|
+
vixsrc_fn, vidlink_fn = _try_vixsrc_movie, _try_vidlink_movie
|
|
60
|
+
vixsrc_ref = f"https://vixsrc.to/embed/movie/{tmdb_id}"
|
|
61
|
+
|
|
53
62
|
if _time.time() < deadline:
|
|
54
63
|
try:
|
|
55
|
-
result =
|
|
64
|
+
result = vixsrc_fn(tmdb_id, quality)
|
|
56
65
|
if result:
|
|
57
|
-
label = quality.upper() if quality else "
|
|
66
|
+
label = quality.upper() if quality else "Auto"
|
|
58
67
|
return ScraperResult(
|
|
59
68
|
title=f"{show} ({label})",
|
|
60
69
|
m3u8_url=result,
|
|
61
|
-
referer=
|
|
70
|
+
referer=vixsrc_ref,
|
|
62
71
|
user_agent=UA,
|
|
63
72
|
)
|
|
64
73
|
except ScraperNetworkError:
|
|
65
74
|
pass
|
|
66
75
|
|
|
67
|
-
# Try vidlink.pro as fallback
|
|
68
76
|
if _time.time() < deadline:
|
|
69
77
|
try:
|
|
70
|
-
result =
|
|
78
|
+
result = vidlink_fn(tmdb_id, quality)
|
|
71
79
|
if result:
|
|
80
|
+
label = quality.upper() if quality else "Auto"
|
|
72
81
|
return ScraperResult(
|
|
73
|
-
title=f"{show} (
|
|
82
|
+
title=f"{show} ({label})",
|
|
74
83
|
m3u8_url=result,
|
|
75
84
|
referer="https://vidlink.pro/",
|
|
76
85
|
user_agent=UA,
|
|
@@ -81,15 +90,22 @@ class WebStreamScraper(BaseScraper):
|
|
|
81
90
|
raise ScraperNoStreamError(self.name, f"No HTTP stream found for {show}")
|
|
82
91
|
|
|
83
92
|
# TV
|
|
93
|
+
if prefer_vidlink:
|
|
94
|
+
vixsrc_fn, vidlink_fn = _try_vidlink, _try_vixsrc
|
|
95
|
+
vixsrc_ref = f"https://vixsrc.to/embed/tv/{tmdb_id}/{season}/{episode}"
|
|
96
|
+
else:
|
|
97
|
+
vixsrc_fn, vidlink_fn = _try_vixsrc, _try_vidlink
|
|
98
|
+
vixsrc_ref = f"https://vixsrc.to/embed/tv/{tmdb_id}/{season}/{episode}"
|
|
99
|
+
|
|
84
100
|
if _time.time() < deadline:
|
|
85
101
|
try:
|
|
86
|
-
result =
|
|
102
|
+
result = vixsrc_fn(tmdb_id, season, episode, quality)
|
|
87
103
|
if result:
|
|
88
|
-
label = quality.upper() if quality else "
|
|
104
|
+
label = quality.upper() if quality else "Auto"
|
|
89
105
|
return ScraperResult(
|
|
90
106
|
title=f"{show} S{season:02d}E{episode:02d} ({label})",
|
|
91
107
|
m3u8_url=result,
|
|
92
|
-
referer=
|
|
108
|
+
referer=vixsrc_ref,
|
|
93
109
|
user_agent=UA,
|
|
94
110
|
)
|
|
95
111
|
except ScraperNetworkError:
|
|
@@ -97,10 +113,11 @@ class WebStreamScraper(BaseScraper):
|
|
|
97
113
|
|
|
98
114
|
if _time.time() < deadline:
|
|
99
115
|
try:
|
|
100
|
-
result =
|
|
116
|
+
result = vidlink_fn(tmdb_id, season, episode, quality)
|
|
101
117
|
if result:
|
|
118
|
+
label = quality.upper() if quality else "Auto"
|
|
102
119
|
return ScraperResult(
|
|
103
|
-
title=f"{show} S{season:02d}E{episode:02d} (
|
|
120
|
+
title=f"{show} S{season:02d}E{episode:02d} ({label})",
|
|
104
121
|
m3u8_url=result,
|
|
105
122
|
referer="https://vidlink.pro/",
|
|
106
123
|
user_agent=UA,
|
|
@@ -177,7 +194,23 @@ def _try_vixsrc(tmdb_id, season, episode, quality="") -> Optional[str]:
|
|
|
177
194
|
return match[1]
|
|
178
195
|
|
|
179
196
|
|
|
180
|
-
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]:
|
|
181
214
|
session = requests.Session()
|
|
182
215
|
session.headers.update({"User-Agent": UA})
|
|
183
216
|
|
|
@@ -205,11 +238,7 @@ def _try_vidlink(tmdb_id, season, episode) -> Optional[str]:
|
|
|
205
238
|
data = stream_resp.json()
|
|
206
239
|
except ValueError:
|
|
207
240
|
raise ScraperNetworkError("vidlink", "Non-JSON response from stream API")
|
|
208
|
-
|
|
209
|
-
if not playlist:
|
|
210
|
-
raise ScraperNetworkError("vidlink", "No playlist in stream response")
|
|
211
|
-
|
|
212
|
-
return playlist
|
|
241
|
+
return _vidlink_quality_url(data, quality)
|
|
213
242
|
|
|
214
243
|
|
|
215
244
|
def _try_vixsrc_movie(tmdb_id, quality="") -> Optional[str]:
|
|
@@ -274,7 +303,7 @@ def _try_vixsrc_movie(tmdb_id, quality="") -> Optional[str]:
|
|
|
274
303
|
return match[1]
|
|
275
304
|
|
|
276
305
|
|
|
277
|
-
def _try_vidlink_movie(tmdb_id) -> Optional[str]:
|
|
306
|
+
def _try_vidlink_movie(tmdb_id, quality="") -> Optional[str]:
|
|
278
307
|
session = requests.Session()
|
|
279
308
|
session.headers.update({"User-Agent": UA})
|
|
280
309
|
|
|
@@ -302,8 +331,4 @@ def _try_vidlink_movie(tmdb_id) -> Optional[str]:
|
|
|
302
331
|
data = stream_resp.json()
|
|
303
332
|
except ValueError:
|
|
304
333
|
raise ScraperNetworkError("vidlink", "Non-JSON response from stream API")
|
|
305
|
-
|
|
306
|
-
if not playlist:
|
|
307
|
-
raise ScraperNetworkError("vidlink", "No playlist in stream response")
|
|
308
|
-
|
|
309
|
-
return playlist
|
|
334
|
+
return _vidlink_quality_url(data, quality)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cinnamon-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.30
|
|
4
4
|
Summary: CLI to search TV shows, movies & anime via TMDB/AniList, scrape m3u8 links, and play in VLC/mpv
|
|
5
5
|
Author: pizza-droid
|
|
6
6
|
License: CC BY-NC 4.0
|
|
@@ -78,8 +78,8 @@ By default you'll be asked whether to watch sub or dub. Skip the prompt with
|
|
|
78
78
|
`-sub` / `-dub`:
|
|
79
79
|
|
|
80
80
|
```bash
|
|
81
|
-
cinnamon anime "Chainsaw Man" --
|
|
82
|
-
cinnamon anime "Frieren" --
|
|
81
|
+
cinnamon anime "Chainsaw Man" --sub
|
|
82
|
+
cinnamon anime "Frieren" --dub
|
|
83
83
|
```
|
|
84
84
|
|
|
85
85
|
|
|
@@ -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.30"
|
|
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.28"
|
|
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
|