cinnamon-cli 0.2.32__tar.gz → 0.2.33__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.32/cinnamon_cli.egg-info → cinnamon_cli-0.2.33}/PKG-INFO +1 -1
- cinnamon_cli-0.2.33/cinnamon/__init__.py +1 -0
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/cinnamon/cli.py +149 -10
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33/cinnamon_cli.egg-info}/PKG-INFO +1 -1
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/pyproject.toml +1 -1
- cinnamon_cli-0.2.32/cinnamon/__init__.py +0 -1
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/LICENSE +0 -0
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/README.md +0 -0
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/cinnamon/anilist.py +0 -0
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/cinnamon/config.py +0 -0
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/cinnamon/downloads.py +0 -0
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/cinnamon/errors.py +0 -0
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/cinnamon/history.py +0 -0
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/cinnamon/player.py +0 -0
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/cinnamon/scrapers/__init__.py +0 -0
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/cinnamon/scrapers/anime.py +0 -0
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/cinnamon/scrapers/base.py +0 -0
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/cinnamon/scrapers/torrentio.py +0 -0
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/cinnamon/scrapers/vidsrc.py +0 -0
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/cinnamon/scrapers/webstream.py +0 -0
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/cinnamon/tmdb.py +0 -0
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/cinnamon/tui.py +0 -0
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/cinnamon_cli.egg-info/SOURCES.txt +0 -0
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/cinnamon_cli.egg-info/dependency_links.txt +0 -0
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/cinnamon_cli.egg-info/entry_points.txt +0 -0
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/cinnamon_cli.egg-info/requires.txt +0 -0
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/cinnamon_cli.egg-info/top_level.txt +0 -0
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/setup.cfg +0 -0
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/tests/test_directstream.py +0 -0
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/tests/test_directstream2.py +0 -0
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/tests/test_directstream3.py +0 -0
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/tests/test_directstream4.py +0 -0
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/tests/test_directstream5.py +0 -0
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/tests/test_directstream6.py +0 -0
- {cinnamon_cli-0.2.32 → cinnamon_cli-0.2.33}/tests/test_ui_smoke.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.33"
|
|
@@ -160,6 +160,141 @@ def _prompt(message, default=None, password=False):
|
|
|
160
160
|
return default
|
|
161
161
|
|
|
162
162
|
|
|
163
|
+
def _pick_episode_keys(items, label_fn, message):
|
|
164
|
+
"""Keyboard-driven episode picker.
|
|
165
|
+
|
|
166
|
+
Press a number once to *select* (highlight) that episode; press the same
|
|
167
|
+
number again to *confirm* and play it. Arrow keys / j / k move the
|
|
168
|
+
highlight, Enter confirms the highlighted episode, and q / esc abort.
|
|
169
|
+
|
|
170
|
+
Multi-digit entry works too: typing "1" then "2" highlights episode 12,
|
|
171
|
+
and repeating the final digit (or pressing Enter) confirms it.
|
|
172
|
+
"""
|
|
173
|
+
if not items:
|
|
174
|
+
return None
|
|
175
|
+
n = len(items)
|
|
176
|
+
|
|
177
|
+
try:
|
|
178
|
+
from prompt_toolkit import Application
|
|
179
|
+
from prompt_toolkit.layout import Layout, Window
|
|
180
|
+
from prompt_toolkit.layout.controls import FormattedTextControl
|
|
181
|
+
from prompt_toolkit.key_binding import KeyBindings
|
|
182
|
+
except Exception:
|
|
183
|
+
return None
|
|
184
|
+
|
|
185
|
+
t = get_theme()
|
|
186
|
+
accent = _q_hex(t.get("accent", "orange1"))
|
|
187
|
+
dim = _q_hex(t.get("dim", "grey50"))
|
|
188
|
+
|
|
189
|
+
class _State:
|
|
190
|
+
sel = 0
|
|
191
|
+
buffer = ""
|
|
192
|
+
last_key = None
|
|
193
|
+
last_press = 0.0
|
|
194
|
+
result = None
|
|
195
|
+
abort = False
|
|
196
|
+
|
|
197
|
+
state = _State()
|
|
198
|
+
app_ref = [None]
|
|
199
|
+
|
|
200
|
+
def _render():
|
|
201
|
+
lines = [(f"bold fg:{accent}", f"{message}\n")]
|
|
202
|
+
lines.append((f"fg:{dim}", " press a number to select · press it again to play · ↑↓/jk move · enter play · q quit\n\n"))
|
|
203
|
+
start = max(0, min(state.sel - 7, n - 15))
|
|
204
|
+
end = min(n, start + 15)
|
|
205
|
+
for i in range(start, end):
|
|
206
|
+
ep = items[i]
|
|
207
|
+
label = label_fn(ep)
|
|
208
|
+
num = f"{i + 1:>3}"
|
|
209
|
+
if i == state.sel:
|
|
210
|
+
if state.buffer:
|
|
211
|
+
lines.append((f"bold fg:{accent}", f" {_POINTER} {num} {label}\n"))
|
|
212
|
+
else:
|
|
213
|
+
lines.append((f"bold fg:{accent}", f" {_POINTER} {num} {label}\n"))
|
|
214
|
+
else:
|
|
215
|
+
lines.append((f"fg:{dim}", f" {num} {label}\n"))
|
|
216
|
+
return lines
|
|
217
|
+
|
|
218
|
+
control = FormattedTextControl(_render)
|
|
219
|
+
|
|
220
|
+
def _confirm():
|
|
221
|
+
state.result = items[state.sel]
|
|
222
|
+
if app_ref[0] is not None:
|
|
223
|
+
app_ref[0].exit()
|
|
224
|
+
|
|
225
|
+
def _abort():
|
|
226
|
+
state.abort = True
|
|
227
|
+
if app_ref[0] is not None:
|
|
228
|
+
app_ref[0].exit()
|
|
229
|
+
|
|
230
|
+
def _press_digit(d):
|
|
231
|
+
now = _time.time()
|
|
232
|
+
if state.last_key is None or (now - state.last_press) > 1.2:
|
|
233
|
+
state.buffer = str(d)
|
|
234
|
+
else:
|
|
235
|
+
state.buffer += str(d)
|
|
236
|
+
state.last_press = now
|
|
237
|
+
last_key = state.last_key
|
|
238
|
+
state.last_key = d
|
|
239
|
+
try:
|
|
240
|
+
idx = int(state.buffer) - 1
|
|
241
|
+
except ValueError:
|
|
242
|
+
idx = -1
|
|
243
|
+
if 0 <= idx < n:
|
|
244
|
+
state.sel = idx
|
|
245
|
+
# Pressing the same digit twice in a row confirms the highlight.
|
|
246
|
+
if d == last_key:
|
|
247
|
+
_confirm()
|
|
248
|
+
return
|
|
249
|
+
if app_ref[0] is not None:
|
|
250
|
+
app_ref[0].invalidate()
|
|
251
|
+
|
|
252
|
+
kb = KeyBindings()
|
|
253
|
+
|
|
254
|
+
for d in range(10):
|
|
255
|
+
kb.add(str(d))(lambda event, d=d: _press_digit(d))
|
|
256
|
+
|
|
257
|
+
@kb.add("up")
|
|
258
|
+
@kb.add("k")
|
|
259
|
+
def _up(event):
|
|
260
|
+
state.buffer = ""
|
|
261
|
+
state.last_key = None
|
|
262
|
+
state.sel = (state.sel - 1) % n
|
|
263
|
+
event.app.invalidate()
|
|
264
|
+
|
|
265
|
+
@kb.add("down")
|
|
266
|
+
@kb.add("j")
|
|
267
|
+
def _down(event):
|
|
268
|
+
state.buffer = ""
|
|
269
|
+
state.last_key = None
|
|
270
|
+
state.sel = (state.sel + 1) % n
|
|
271
|
+
event.app.invalidate()
|
|
272
|
+
|
|
273
|
+
@kb.add("enter")
|
|
274
|
+
def _enter(event):
|
|
275
|
+
_confirm()
|
|
276
|
+
|
|
277
|
+
@kb.add("q")
|
|
278
|
+
@kb.add("escape")
|
|
279
|
+
def _quit(event):
|
|
280
|
+
_abort()
|
|
281
|
+
|
|
282
|
+
app = Application(
|
|
283
|
+
layout=Layout(Window(control, always_scroll=True)),
|
|
284
|
+
key_bindings=kb,
|
|
285
|
+
mouse_support=False,
|
|
286
|
+
full_screen=False,
|
|
287
|
+
)
|
|
288
|
+
app_ref[0] = app
|
|
289
|
+
try:
|
|
290
|
+
app.run()
|
|
291
|
+
except Exception:
|
|
292
|
+
return None
|
|
293
|
+
if state.abort:
|
|
294
|
+
return None
|
|
295
|
+
return state.result
|
|
296
|
+
|
|
297
|
+
|
|
163
298
|
_UPDATE_CHECK_CACHE = 86400 # 24 hours
|
|
164
299
|
|
|
165
300
|
|
|
@@ -1153,15 +1288,15 @@ def _interactive_episode_picker(tmdb, show, scraper, player, quality, info_only,
|
|
|
1153
1288
|
return
|
|
1154
1289
|
|
|
1155
1290
|
try:
|
|
1156
|
-
|
|
1157
|
-
for ep in episodes:
|
|
1291
|
+
def _ep_label(ep):
|
|
1158
1292
|
ep_num = ep.get("episode_number", "?")
|
|
1159
1293
|
ep_title = ep.get("name", f"Episode {ep_num}")
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
ep_chosen =
|
|
1163
|
-
|
|
1164
|
-
|
|
1294
|
+
return f"E{ep_num:02d} {ep_title}" if isinstance(ep_num, int) else f"{ep_num} {ep_title}"
|
|
1295
|
+
|
|
1296
|
+
ep_chosen = _pick_episode_keys(
|
|
1297
|
+
episodes,
|
|
1298
|
+
_ep_label,
|
|
1299
|
+
f"Season {season_num} — select an episode",
|
|
1165
1300
|
)
|
|
1166
1301
|
if not ep_chosen:
|
|
1167
1302
|
return
|
|
@@ -1680,9 +1815,13 @@ def _run_anime_flow(show_name, episodes_detail, season=None, ep_str=None, player
|
|
|
1680
1815
|
ep_end = max_ep
|
|
1681
1816
|
else:
|
|
1682
1817
|
try:
|
|
1683
|
-
|
|
1684
|
-
ep_chosen =
|
|
1685
|
-
|
|
1818
|
+
sorted_eps = sorted(episodes)
|
|
1819
|
+
ep_chosen = _pick_episode_keys(
|
|
1820
|
+
sorted_eps,
|
|
1821
|
+
lambda e: f"Episode {e}",
|
|
1822
|
+
"Select an episode",
|
|
1823
|
+
)
|
|
1824
|
+
if ep_chosen is None:
|
|
1686
1825
|
return
|
|
1687
1826
|
ep_start = int(ep_chosen)
|
|
1688
1827
|
except Exception:
|
|
@@ -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.33"
|
|
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.32"
|
|
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
|
|
File without changes
|