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.
Files changed (35) hide show
  1. {cinnamon_cli-0.2.28/cinnamon_cli.egg-info → cinnamon_cli-0.2.30}/PKG-INFO +3 -3
  2. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/README.md +2 -2
  3. cinnamon_cli-0.2.30/cinnamon/__init__.py +1 -0
  4. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon/cli.py +2 -0
  5. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon/scrapers/anime.py +1 -1
  6. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon/scrapers/webstream.py +49 -24
  7. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30/cinnamon_cli.egg-info}/PKG-INFO +3 -3
  8. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/pyproject.toml +1 -1
  9. cinnamon_cli-0.2.28/cinnamon/__init__.py +0 -1
  10. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/LICENSE +0 -0
  11. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon/anilist.py +0 -0
  12. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon/config.py +0 -0
  13. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon/downloads.py +0 -0
  14. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon/errors.py +0 -0
  15. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon/history.py +0 -0
  16. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon/player.py +0 -0
  17. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon/scrapers/__init__.py +0 -0
  18. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon/scrapers/base.py +0 -0
  19. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon/scrapers/torrentio.py +0 -0
  20. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon/scrapers/vidsrc.py +0 -0
  21. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon/tmdb.py +0 -0
  22. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon/tui.py +0 -0
  23. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon_cli.egg-info/SOURCES.txt +0 -0
  24. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon_cli.egg-info/dependency_links.txt +0 -0
  25. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon_cli.egg-info/entry_points.txt +0 -0
  26. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon_cli.egg-info/requires.txt +0 -0
  27. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/cinnamon_cli.egg-info/top_level.txt +0 -0
  28. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/setup.cfg +0 -0
  29. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/tests/test_directstream.py +0 -0
  30. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/tests/test_directstream2.py +0 -0
  31. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/tests/test_directstream3.py +0 -0
  32. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/tests/test_directstream4.py +0 -0
  33. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/tests/test_directstream5.py +0 -0
  34. {cinnamon_cli-0.2.28 → cinnamon_cli-0.2.30}/tests/test_directstream6.py +0 -0
  35. {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.28
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" --dub
82
- cinnamon anime "Frieren" --sub
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" --dub
53
- cinnamon anime "Frieren" --sub
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 = quality.upper() if quality else "Mp4upload"
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
- # Try vixsrc.to first
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 = _try_vixsrc_movie(tmdb_id, quality)
64
+ result = vixsrc_fn(tmdb_id, quality)
56
65
  if result:
57
- label = quality.upper() if quality else "vixsrc"
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=f"https://vixsrc.to/embed/movie/{tmdb_id}",
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 = _try_vidlink_movie(tmdb_id)
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} (vidlink)",
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 = _try_vixsrc(tmdb_id, season, episode, quality)
102
+ result = vixsrc_fn(tmdb_id, season, episode, quality)
87
103
  if result:
88
- label = quality.upper() if quality else "vixsrc"
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=f"https://vixsrc.to/embed/tv/{tmdb_id}/{season}/{episode}",
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 = _try_vidlink(tmdb_id, season, episode)
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} (vidlink)",
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 _try_vidlink(tmdb_id, season, episode) -> Optional[str]:
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
- playlist = data.get("stream", {}).get("playlist")
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
- playlist = data.get("stream", {}).get("playlist")
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.28
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" --dub
82
- cinnamon anime "Frieren" --sub
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.28"
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