cinnamon-cli 0.2.39__tar.gz → 0.3.0__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 (38) hide show
  1. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/PKG-INFO +1 -1
  2. cinnamon_cli-0.3.0/cinnamon/__init__.py +1 -0
  3. cinnamon_cli-0.3.0/cinnamon/anilist.py +196 -0
  4. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/cinnamon/cli.py +775 -122
  5. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/cinnamon/scrapers/anime.py +75 -33
  6. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/cinnamon/tmdb.py +3 -0
  7. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/cinnamon_cli.egg-info/PKG-INFO +1 -1
  8. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/pyproject.toml +1 -1
  9. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/tests/test_ui_smoke.py +1 -1
  10. cinnamon_cli-0.2.39/cinnamon/__init__.py +0 -1
  11. cinnamon_cli-0.2.39/cinnamon/anilist.py +0 -92
  12. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/LICENSE +0 -0
  13. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/README.md +0 -0
  14. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/cinnamon/config.py +0 -0
  15. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/cinnamon/downloads.py +0 -0
  16. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/cinnamon/embedapi.py +0 -0
  17. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/cinnamon/errors.py +0 -0
  18. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/cinnamon/history.py +0 -0
  19. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/cinnamon/player.py +0 -0
  20. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/cinnamon/scrapers/__init__.py +0 -0
  21. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/cinnamon/scrapers/_vidlink_crypto.py +0 -0
  22. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/cinnamon/scrapers/base.py +0 -0
  23. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/cinnamon/scrapers/torrentio.py +0 -0
  24. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/cinnamon/scrapers/vidsrc.py +0 -0
  25. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/cinnamon/scrapers/webstream.py +0 -0
  26. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/cinnamon/tui.py +0 -0
  27. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/cinnamon_cli.egg-info/SOURCES.txt +0 -0
  28. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/cinnamon_cli.egg-info/dependency_links.txt +0 -0
  29. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/cinnamon_cli.egg-info/entry_points.txt +0 -0
  30. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/cinnamon_cli.egg-info/requires.txt +0 -0
  31. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/cinnamon_cli.egg-info/top_level.txt +0 -0
  32. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/setup.cfg +0 -0
  33. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/tests/test_directstream.py +0 -0
  34. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/tests/test_directstream2.py +0 -0
  35. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/tests/test_directstream3.py +0 -0
  36. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/tests/test_directstream4.py +0 -0
  37. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/tests/test_directstream5.py +0 -0
  38. {cinnamon_cli-0.2.39 → cinnamon_cli-0.3.0}/tests/test_directstream6.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cinnamon-cli
3
- Version: 0.2.39
3
+ Version: 0.3.0
4
4
  Summary: Watch Movies, TV shows and anime from your terminal.
5
5
  Author: pizza-droid
6
6
  License: CC BY-NC 4.0
@@ -0,0 +1 @@
1
+ __version__ = "0.3.0"
@@ -0,0 +1,196 @@
1
+ import time as _time
2
+
3
+ import requests
4
+
5
+ from .errors import CinnamonError
6
+
7
+ API = "https://graphql.anilist.co"
8
+ UA = "cinnamon/0.1.0"
9
+
10
+ _SEARCH_QUERY = """
11
+ query ($search: String, $page: Int) {
12
+ Page(page: $page, perPage: 20) {
13
+ media(search: $search, type: ANIME) {
14
+ id
15
+ idMal
16
+ title { romaji english native }
17
+ episodes
18
+ status
19
+ format
20
+ startDate { year }
21
+ genres
22
+ }
23
+ }
24
+ }
25
+ """
26
+
27
+
28
+ _RELATION_QUERY = """
29
+ query ($search: String) {
30
+ Page(page: 1, perPage: 5) {
31
+ media(search: $search, type: ANIME) {
32
+ id
33
+ title { romaji english }
34
+ relations {
35
+ edges {
36
+ relationType
37
+ node {
38
+ id
39
+ title { romaji english }
40
+ episodes
41
+ status
42
+ format
43
+ }
44
+ }
45
+ }
46
+ }
47
+ }
48
+ }
49
+ """
50
+
51
+
52
+ def search_anime(query):
53
+ try:
54
+ resp = requests.post(
55
+ API,
56
+ json={"query": _SEARCH_QUERY, "variables": {"search": query, "page": 1}},
57
+ timeout=15,
58
+ headers={"User-Agent": UA, "Content-Type": "application/json", "Accept": "application/json"},
59
+ )
60
+ resp.raise_for_status()
61
+ data = resp.json()
62
+ return data.get("data", {}).get("Page", {}).get("media", [])
63
+ except requests.ConnectionError:
64
+ raise CinnamonError("Could not reach AniList. Check your internet connection.")
65
+ except requests.Timeout:
66
+ raise CinnamonError("AniList request timed out.")
67
+ except requests.HTTPError as e:
68
+ raise CinnamonError(f"AniList returned an error (HTTP {e.response.status_code}).")
69
+
70
+
71
+ def find_sequel(anime_name):
72
+ """Search AniList for an anime and return its first SEQUEL relation, or None."""
73
+ try:
74
+ resp = requests.post(
75
+ API,
76
+ json={"query": _RELATION_QUERY, "variables": {"search": anime_name}},
77
+ timeout=15,
78
+ headers={"User-Agent": UA, "Content-Type": "application/json", "Accept": "application/json"},
79
+ )
80
+ resp.raise_for_status()
81
+ data = resp.json()
82
+ media_list = data.get("data", {}).get("Page", {}).get("media", [])
83
+ for m in media_list:
84
+ edges = m.get("relations", {}).get("edges", [])
85
+ for e in edges:
86
+ if e.get("relationType") == "SEQUEL":
87
+ node = e.get("node", {})
88
+ title = node.get("title", {})
89
+ name = title.get("romaji") or title.get("english") or ""
90
+ if name:
91
+ return {"id": node["id"], "name": name}
92
+ return None
93
+ except (requests.RequestException, CinnamonError):
94
+ return None
95
+
96
+
97
+ _SCHEDULE_QUERY = """
98
+ query ($page: Int, $perPage: Int, $airingAtGreater: Int, $airingAtLesser: Int) {
99
+ Page(page: $page, perPage: $perPage) {
100
+ airingSchedules(
101
+ airingAt_greater: $airingAtGreater
102
+ airingAt_lesser: $airingAtLesser
103
+ sort: TIME
104
+ ) {
105
+ id
106
+ airingAt
107
+ episode
108
+ media {
109
+ id
110
+ title { romaji english }
111
+ format
112
+ status
113
+ episodes
114
+ popularity
115
+ averageScore
116
+ }
117
+ }
118
+ }
119
+ }
120
+ """
121
+
122
+
123
+ _MEDIA_AIRING_QUERY = """
124
+ query ($mediaId: Int) {
125
+ Page(page: 1, perPage: 100) {
126
+ airingSchedules(mediaId: $mediaId, sort: EPISODE) {
127
+ airingAt
128
+ episode
129
+ timeUntilAiring
130
+ }
131
+ }
132
+ }
133
+ """
134
+
135
+
136
+ def get_media_airing(media_id):
137
+ """Return list of {airingAt, episode, timeUntilAiring} for a given media ID."""
138
+ try:
139
+ resp = requests.post(
140
+ API,
141
+ json={"query": _MEDIA_AIRING_QUERY, "variables": {"mediaId": media_id}},
142
+ timeout=15,
143
+ headers={"User-Agent": UA, "Content-Type": "application/json", "Accept": "application/json"},
144
+ )
145
+ resp.raise_for_status()
146
+ data = resp.json()
147
+ return data.get("data", {}).get("Page", {}).get("airingSchedules", [])
148
+ except requests.ConnectionError:
149
+ return []
150
+ except requests.Timeout:
151
+ return []
152
+ except requests.HTTPError:
153
+ return []
154
+
155
+
156
+ def get_schedule(days=7):
157
+ now = int(_time.time())
158
+ end = now + (days * 86400)
159
+ try:
160
+ resp = requests.post(
161
+ API,
162
+ json={
163
+ "query": _SCHEDULE_QUERY,
164
+ "variables": {
165
+ "page": 1,
166
+ "perPage": 50,
167
+ "airingAtGreater": now,
168
+ "airingAtLesser": end,
169
+ },
170
+ },
171
+ timeout=15,
172
+ headers={"User-Agent": UA, "Content-Type": "application/json", "Accept": "application/json"},
173
+ )
174
+ resp.raise_for_status()
175
+ data = resp.json()
176
+ schedules = data.get("data", {}).get("Page", {}).get("airingSchedules", [])
177
+ results = []
178
+ for s in schedules:
179
+ media = s.get("media", {})
180
+ title = media.get("title", {}) or {}
181
+ results.append({
182
+ "airing_at": s.get("airingAt", 0),
183
+ "episode": s.get("episode"),
184
+ "title_romaji": title.get("romaji", "") or "",
185
+ "title_english": title.get("english", "") or "",
186
+ "format": media.get("format", ""),
187
+ "popularity": media.get("popularity", 0) or 0,
188
+ "score": media.get("averageScore", 0) or 0,
189
+ })
190
+ return results
191
+ except requests.ConnectionError:
192
+ raise CinnamonError("Could not reach AniList. Check your internet connection.")
193
+ except requests.Timeout:
194
+ raise CinnamonError("AniList request timed out.")
195
+ except requests.HTTPError as e:
196
+ raise CinnamonError(f"AniList returned an error (HTTP {e.response.status_code}).")