cinebox 0.1.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.
- cinebox-0.1.0/PKG-INFO +335 -0
- cinebox-0.1.0/README.md +297 -0
- cinebox-0.1.0/pyproject.toml +87 -0
- cinebox-0.1.0/pyproject.toml.orig +77 -0
- cinebox-0.1.0/src/cinebox/__init__.py +808 -0
- cinebox-0.1.0/src/cinebox/__main__.py +4 -0
- cinebox-0.1.0/src/cinebox/config.py +192 -0
- cinebox-0.1.0/src/cinebox/discord_rpc.py +67 -0
- cinebox-0.1.0/src/cinebox/downloader.py +117 -0
- cinebox-0.1.0/src/cinebox/favorites.py +72 -0
- cinebox-0.1.0/src/cinebox/history.py +67 -0
- cinebox-0.1.0/src/cinebox/metadata.py +122 -0
- cinebox-0.1.0/src/cinebox/models.py +129 -0
- cinebox-0.1.0/src/cinebox/player.py +96 -0
- cinebox-0.1.0/src/cinebox/providers/__init__.py +27 -0
- cinebox-0.1.0/src/cinebox/providers/base.py +126 -0
- cinebox-0.1.0/src/cinebox/providers/discovery.py +76 -0
- cinebox-0.1.0/src/cinebox/providers/imdb_su.py +183 -0
- cinebox-0.1.0/src/cinebox/providers/keyword.py +67 -0
- cinebox-0.1.0/src/cinebox/providers/multiembed.py +44 -0
- cinebox-0.1.0/src/cinebox/providers/vidsrc.py +126 -0
- cinebox-0.1.0/src/cinebox/search.py +92 -0
- cinebox-0.1.0/src/cinebox/settings.py +160 -0
- cinebox-0.1.0/src/cinebox/subtitles.py +698 -0
- cinebox-0.1.0/src/cinebox/ui/__init__.py +8 -0
- cinebox-0.1.0/src/cinebox/ui/art.py +60 -0
- cinebox-0.1.0/src/cinebox/ui/components.py +718 -0
- cinebox-0.1.0/src/cinebox/ui/poster.py +110 -0
- cinebox-0.1.0/src/cinebox/ui/terminal.py +153 -0
- cinebox-0.1.0/src/cinebox/ui/themes.py +73 -0
- cinebox-0.1.0/src/cinebox/utils.py +131 -0
cinebox-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cinebox
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Terminal tool to search, stream, and download movies & TV shows
|
|
5
|
+
Keywords: movie,streaming,hls,mpv,vlc,imdb,cli,terminal,tv,video
|
|
6
|
+
Author: Hossam Gamal
|
|
7
|
+
Author-email: Hossam Gamal <hosamgamal33345@gmail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Topic :: Multimedia :: Video
|
|
20
|
+
Classifier: Topic :: Terminals
|
|
21
|
+
Requires-Dist: httpx>=0.27
|
|
22
|
+
Requires-Dist: rich>=13.0
|
|
23
|
+
Requires-Dist: click>=8.1
|
|
24
|
+
Requires-Dist: pydantic>=2.0
|
|
25
|
+
Requires-Dist: pillow>=10.0
|
|
26
|
+
Requires-Dist: numpy>=1.24
|
|
27
|
+
Requires-Dist: pypresence>=4.5 ; extra == 'discord'
|
|
28
|
+
Requires-Dist: pillow>=10.0 ; extra == 'posters'
|
|
29
|
+
Requires-Dist: numpy>=1.24 ; extra == 'posters'
|
|
30
|
+
Requires-Python: >=3.11
|
|
31
|
+
Project-URL: Homepage, https://github.com/hosam00/cinebox
|
|
32
|
+
Project-URL: Repository, https://github.com/hosam00/cinebox
|
|
33
|
+
Project-URL: Issues, https://github.com/hosam00/cinebox/issues
|
|
34
|
+
Project-URL: Documentation, https://github.com/hosam00/cinebox#readme
|
|
35
|
+
Provides-Extra: discord
|
|
36
|
+
Provides-Extra: posters
|
|
37
|
+
Description-Content-Type: text/markdown
|
|
38
|
+
|
|
39
|
+
<div align="center">
|
|
40
|
+
|
|
41
|
+
# 🎬 cinebox
|
|
42
|
+
|
|
43
|
+
**A terminal-based movie streaming tool. Like ani-cli, but for cinema.**
|
|
44
|
+
|
|
45
|
+
[](https://www.python.org/)
|
|
46
|
+
[](LICENSE)
|
|
47
|
+
[](https://github.com/astral-sh/ruff)
|
|
48
|
+
[]()
|
|
49
|
+
[](https://pypi.org/project/cinebox/)
|
|
50
|
+
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<p align="center">
|
|
54
|
+
<em>record with vhs / asciinema — replace <code>docs/demo.gif</code> with your recording</em>
|
|
55
|
+
</p>
|
|
56
|
+
|
|
57
|
+
<p align="center">
|
|
58
|
+
<img src="docs/demo.gif" alt="cinebox demo — search, stream, and play in the terminal" width="700" />
|
|
59
|
+
</p>
|
|
60
|
+
|
|
61
|
+
A terminal-based tool to search, stream, and download movies and TV shows — inspired by [ani-cli](https://github.com/pystardust/ani-cli), built in Python with a rich interactive TUI and a provider-based streaming architecture.
|
|
62
|
+
|
|
63
|
+
Watch in **mpv** or **VLC**, download via **yt-dlp**, fetch **subtitles** — all without a single API key.
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
┌─────────┐ Rich TUI ┌──────────┐ resolve ┌──────────────┐ play ┌───────┐
|
|
67
|
+
│ IMDb │ ─────────────▶│ cinebox │ ─────────────▶│ provider │──────────▶│ mpv │
|
|
68
|
+
│ suggest │ └──────────┘ │ (imdb_su · │ │ vlc │
|
|
69
|
+
└─────────┘ │ │ vidsrc · …) │ └───────┘
|
|
70
|
+
└──▶ yt-dlp ───▶ .mp4/.mkv (download)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## ✨ Features
|
|
74
|
+
|
|
75
|
+
- **No API keys, no accounts** — search runs against IMDb's public suggestion endpoint.
|
|
76
|
+
- **Rich TUI** — arrow-key navigation, themed panels, full-screen menus, ANSI poster art.
|
|
77
|
+
- **Stream or download** movies and TV episodes via a provider fallback chain.
|
|
78
|
+
- **Batch download** — mark multiple TV episodes and grab them all at once.
|
|
79
|
+
- **Subtitles** — anonymous fetching for movies and TV (Arabic + more via TUI settings).
|
|
80
|
+
- **History & Favorites** — relaunch titles from a watch list or save them for later.
|
|
81
|
+
- **Settings menu** — configure player, download paths, quality, theme, and subtitles from the TUI.
|
|
82
|
+
- **17 color themes** — blue, red, purple, sunset, mint, coral, and more.
|
|
83
|
+
- **Discord Rich Presence** — now-playing status (optional, needs a client ID).
|
|
84
|
+
- **Headless-friendly** — numbered menu fallback for piped/SSH sessions.
|
|
85
|
+
- **Open to extensions** — drop a new provider file into `providers/` and it registers itself automatically.
|
|
86
|
+
|
|
87
|
+
## 📦 Requirements
|
|
88
|
+
|
|
89
|
+
- Python 3.11+
|
|
90
|
+
- A media player: **mpv** (default) or **vlc**
|
|
91
|
+
- `yt-dlp` + `ffmpeg` on your `PATH` for downloads
|
|
92
|
+
- Optional: `pypresence` for Discord Rich Presence
|
|
93
|
+
|
|
94
|
+
## 🚀 Installation
|
|
95
|
+
|
|
96
|
+
### PyPI (recommended)
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
pipx install cinebox # cleanest: isolated tool install
|
|
100
|
+
uv tool install cinebox # same thing, uv-managed
|
|
101
|
+
pip install cinebox # or plain pip
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
> **`pipx`** keeps cinebox and its dependencies in an isolated environment
|
|
105
|
+
> so they never clash with your other Python packages. Install `pipx` with
|
|
106
|
+
> `pipx --help` → `pipx install pipx` if you don't have it yet.
|
|
107
|
+
|
|
108
|
+
### Local checkout (development)
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
git clone https://github.com/hosam00/cinebox
|
|
112
|
+
cd cinebox
|
|
113
|
+
uv sync --group dev # create .venv + install all deps
|
|
114
|
+
uv run cinebox --help # verify
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## 🎮 Quick start
|
|
118
|
+
|
|
119
|
+
### Interactive home screen (TUI)
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
cinebox
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Type a query to search, or use the home keys below. Everything else is driven
|
|
126
|
+
by arrow keys, `Enter`, `D` for download, `S` for settings.
|
|
127
|
+
|
|
128
|
+
### Stream directly by IMDb ID
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
cinebox play tt0133093 # The Matrix (1999)
|
|
132
|
+
cinebox play tt0944947 -t tv -s 1 -e 1 # GoT S01E01
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Download
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
cinebox download "the matrix" -q 720p
|
|
139
|
+
cinebox download "breaking bad" -t tv
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Headless search
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
echo 1 | cinebox search "inception" -t movie
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## ⌨️ Key bindings
|
|
149
|
+
|
|
150
|
+
| Key | Context | Action |
|
|
151
|
+
|-----------|----------------|------------------------------------------|
|
|
152
|
+
| `↑` / `↓` | All menus | Navigate |
|
|
153
|
+
| `Enter` | Results | Play selected |
|
|
154
|
+
| `Enter` | Quality menu | Watch |
|
|
155
|
+
| `D` | Quality menu | Download at highlighted quality |
|
|
156
|
+
| `D` | Episode picker | Download marked (or highlighted) |
|
|
157
|
+
| `Space` | Episode picker | Toggle mark for batch download |
|
|
158
|
+
| `F` | Search results | Toggle favorite |
|
|
159
|
+
| `b` | Any menu | Back |
|
|
160
|
+
| `q` | Any menu | Quit |
|
|
161
|
+
|
|
162
|
+
## ⚙️ Configuration
|
|
163
|
+
|
|
164
|
+
First run creates `~/.config/cinebox/config.toml`. Notable options:
|
|
165
|
+
|
|
166
|
+
```toml
|
|
167
|
+
[player]
|
|
168
|
+
default = "mpv" # mpv | vlc
|
|
169
|
+
fullscreen = true
|
|
170
|
+
extra_args = [] # extra args passed to the player (e.g. ["--mute"])
|
|
171
|
+
|
|
172
|
+
[download]
|
|
173
|
+
movies_dir = "~/Movies"
|
|
174
|
+
tv_dir = "~/Movies/TV"
|
|
175
|
+
quality = "1080p"
|
|
176
|
+
parallel_fragments = 3 # 1–16 concurrent download segments
|
|
177
|
+
|
|
178
|
+
[ui]
|
|
179
|
+
theme = "blue" # one of 17 themes
|
|
180
|
+
|
|
181
|
+
[discord]
|
|
182
|
+
enabled = false # requires a registered client ID
|
|
183
|
+
client_id = ""
|
|
184
|
+
|
|
185
|
+
[subtitle]
|
|
186
|
+
enabled = true
|
|
187
|
+
source = "auto" # auto | provider | yifysubtitles | subsource
|
|
188
|
+
default_lang = "en"
|
|
189
|
+
languages = ["en", "ar"]
|
|
190
|
+
auto_download = false # auto-fetch subs on watch (adds latency)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Any unset key falls back to its default. Everything above the `[discord]`
|
|
194
|
+
section is also editable from the TUI settings menu (`S`).
|
|
195
|
+
|
|
196
|
+
## 🔧 How it works
|
|
197
|
+
|
|
198
|
+
1. **Search** — `GET https://v3.sg.media-imdb.com/suggestion/{char}/{query}.json`
|
|
199
|
+
returns matching titles with IMDb IDs. No auth. Keyword-based providers
|
|
200
|
+
(e.g. Stardima) are merged automatically when present in the provider chain.
|
|
201
|
+
2. **Resolve** — providers turn an IMDb ID into a playable HLS/MP4 URL:
|
|
202
|
+
- `imdb_su` (primary) — follows the embed player chain to the `vaplayer.ru`
|
|
203
|
+
stream API; headers are spoofed so the CDN serves child manifests.
|
|
204
|
+
- `vidsrc` (best-effort) — `vidsrc.to` → `vsembed.ru` → vidsrc data API.
|
|
205
|
+
Encrypted results that need a browser are skipped.
|
|
206
|
+
- `multiembed` (best-effort) — currently Turnstile-gated and skipped.
|
|
207
|
+
Providers are **auto-discovered** when dropped into `src/cinebox/providers/`.
|
|
208
|
+
Set `priority` on your class to control chain order (lower runs first).
|
|
209
|
+
3. **Play** — mpv/VLC receive the URL plus the required `Referer`/`Origin`/
|
|
210
|
+
`User-Agent` headers, so the stream actually plays.
|
|
211
|
+
4. **Download** — the same resolved URL is handed to `yt-dlp` (optionally with
|
|
212
|
+
subtitle muxing) and saved under your configured movies/TV directories.
|
|
213
|
+
|
|
214
|
+
Providers are tried in order; if one fails, the next takes over. Some providers
|
|
215
|
+
can vanish or change overnight — resolution is best-effort by design.
|
|
216
|
+
|
|
217
|
+
## 🗂️ Project layout
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
src/cinebox/
|
|
221
|
+
├── __init__.py click CLI + TUI orchestration (search / play / download)
|
|
222
|
+
├── config.py TOML config: defaults, paths, endpoints
|
|
223
|
+
├── search.py IMDb suggestion endpoint + merged search_all()
|
|
224
|
+
├── models.py pydantic models (SearchResult, StreamInfo, MediaType)
|
|
225
|
+
├── metadata.py best-effort IMDb title-page enrichment (cached)
|
|
226
|
+
├── history.py watch history persistence
|
|
227
|
+
├── favorites.py favorites persistence
|
|
228
|
+
├── settings.py interactive settings catalog + editor
|
|
229
|
+
├── discord_rpc.py optional Discord Rich Presence
|
|
230
|
+
├── utils.py logging, HTTP client, retry helpers
|
|
231
|
+
├── player.py mpv / VLC launch command builders
|
|
232
|
+
├── downloader.py yt-dlp wrapper + quality mapping
|
|
233
|
+
├── subtitles.py anonymous subtitle fetch (movies + TV)
|
|
234
|
+
├── ui/
|
|
235
|
+
│ ├── themes.py 17 color themes
|
|
236
|
+
│ ├── art.py ASCII header + goodbye art
|
|
237
|
+
│ ├── terminal.py raw terminal I/O helpers
|
|
238
|
+
│ ├── components.py menus, loading, episode picker, error panel, layout
|
|
239
|
+
│ └── poster.py ANSI truecolor poster rendering (Pillow + numpy)
|
|
240
|
+
└── providers/
|
|
241
|
+
├── base.py BaseProvider, ProviderRegistry, ResolutionError
|
|
242
|
+
├── discovery.py auto-discovery of concrete providers
|
|
243
|
+
├── keyword.py KeywordProvider ABC for keyword-search platforms
|
|
244
|
+
├── imdb_su.py primary provider (vaplayer stream API)
|
|
245
|
+
├── vidsrc.py best-effort fallback
|
|
246
|
+
└── multiembed.py best-effort fallback (Turnstile-gated)
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## 🧪 Development
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
uv sync --group dev
|
|
253
|
+
uv run pytest # 126 hermetic tests, no network
|
|
254
|
+
uv run ruff check src tests
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
To run the tool headlessly against the live network while developing:
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
env HOME=/tmp/fakehome uv run cinebox search "the matrix" -t movie
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Adding a new provider
|
|
264
|
+
|
|
265
|
+
Drop a Python file into `src/cinebox/providers/` (e.g. `stardima.py`):
|
|
266
|
+
|
|
267
|
+
```python
|
|
268
|
+
from __future__ import annotations
|
|
269
|
+
from cinebox.providers.base import BaseProvider
|
|
270
|
+
from cinebox.models import MediaType, StreamInfo
|
|
271
|
+
|
|
272
|
+
class StardiMaProvider(BaseProvider):
|
|
273
|
+
name = "stardima"
|
|
274
|
+
priority = 40 # lower number = runs first in the chain
|
|
275
|
+
|
|
276
|
+
def resolve(self, imdb_id: str, media_type: MediaType,
|
|
277
|
+
season: int | None = None, episode: int | None = None) -> StreamInfo:
|
|
278
|
+
# ... fetch the stream URL from stardima ...
|
|
279
|
+
...
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
That's it — **no registry edits required**. `discovery.py` picks up any
|
|
283
|
+
concrete `BaseProvider` subclass in the package and adds it to the chain.
|
|
284
|
+
Set `priority` to control position (default `100`; `imdb_su` is `10`,
|
|
285
|
+
`vidsrc` `20`, `multiembed` `30`).
|
|
286
|
+
|
|
287
|
+
#### Keyword-search platforms (e.g. Stardima)
|
|
288
|
+
|
|
289
|
+
If your source resolves streams by raw keywords rather than IMDb IDs,
|
|
290
|
+
subclass `KeywordProvider` instead:
|
|
291
|
+
|
|
292
|
+
```python
|
|
293
|
+
from cinebox.providers.keyword import KeywordProvider
|
|
294
|
+
from cinebox.models import SearchResult
|
|
295
|
+
|
|
296
|
+
class StardiMaProvider(KeywordProvider):
|
|
297
|
+
scheme = "stardima"
|
|
298
|
+
priority = 40
|
|
299
|
+
|
|
300
|
+
def search(self, query, media_type=None):
|
|
301
|
+
# return list[SearchResult] — use self.synthetic_id("12345") for
|
|
302
|
+
# IDs the platform can't map back to IMDb
|
|
303
|
+
...
|
|
304
|
+
|
|
305
|
+
def resolve(self, imdb_id, media_type, season=None, episode=None):
|
|
306
|
+
# imdb_id may be synthetic: "stardima:12345"
|
|
307
|
+
...
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
Keyword providers are merged into search results automatically alongside
|
|
311
|
+
IMDb suggestions. See `src/cinebox/providers/keyword.py` for the full interface.
|
|
312
|
+
|
|
313
|
+
## ✅ Roadmap
|
|
314
|
+
|
|
315
|
+
- [x] PyPI release via Trusted Publishing
|
|
316
|
+
- [x] Keyword-search provider interface (Stardima-ready)
|
|
317
|
+
- [x] Auto-discovery for drop-in providers
|
|
318
|
+
- [ ] `fzf` integration / fuzzy search over history & favorites
|
|
319
|
+
- [ ] More subtitle sources (incl. forced/embedded options)
|
|
320
|
+
- [ ] Provider config: per-provider enable/disable + health checks
|
|
321
|
+
- [ ] A full Stardima keyword provider (PR welcome)
|
|
322
|
+
|
|
323
|
+
## ⚠️ Disclaimer
|
|
324
|
+
|
|
325
|
+
For content you have the right to consume. The tool indexes publicly
|
|
326
|
+
accessible streams and hosts no media. Providers and their CDNs may change or
|
|
327
|
+
break at any time — use at your own risk, and always respect copyright.
|
|
328
|
+
|
|
329
|
+
Read the full [MIT license](LICENSE).
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
<p align="center">
|
|
334
|
+
<sub>Made with ❤️· Rich · yt-dlp · Python</sub>
|
|
335
|
+
</p>
|
cinebox-0.1.0/README.md
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# 🎬 cinebox
|
|
4
|
+
|
|
5
|
+
**A terminal-based movie streaming tool. Like ani-cli, but for cinema.**
|
|
6
|
+
|
|
7
|
+
[](https://www.python.org/)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
[](https://github.com/astral-sh/ruff)
|
|
10
|
+
[]()
|
|
11
|
+
[](https://pypi.org/project/cinebox/)
|
|
12
|
+
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<p align="center">
|
|
16
|
+
<em>record with vhs / asciinema — replace <code>docs/demo.gif</code> with your recording</em>
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<img src="docs/demo.gif" alt="cinebox demo — search, stream, and play in the terminal" width="700" />
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
A terminal-based tool to search, stream, and download movies and TV shows — inspired by [ani-cli](https://github.com/pystardust/ani-cli), built in Python with a rich interactive TUI and a provider-based streaming architecture.
|
|
24
|
+
|
|
25
|
+
Watch in **mpv** or **VLC**, download via **yt-dlp**, fetch **subtitles** — all without a single API key.
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
┌─────────┐ Rich TUI ┌──────────┐ resolve ┌──────────────┐ play ┌───────┐
|
|
29
|
+
│ IMDb │ ─────────────▶│ cinebox │ ─────────────▶│ provider │──────────▶│ mpv │
|
|
30
|
+
│ suggest │ └──────────┘ │ (imdb_su · │ │ vlc │
|
|
31
|
+
└─────────┘ │ │ vidsrc · …) │ └───────┘
|
|
32
|
+
└──▶ yt-dlp ───▶ .mp4/.mkv (download)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## ✨ Features
|
|
36
|
+
|
|
37
|
+
- **No API keys, no accounts** — search runs against IMDb's public suggestion endpoint.
|
|
38
|
+
- **Rich TUI** — arrow-key navigation, themed panels, full-screen menus, ANSI poster art.
|
|
39
|
+
- **Stream or download** movies and TV episodes via a provider fallback chain.
|
|
40
|
+
- **Batch download** — mark multiple TV episodes and grab them all at once.
|
|
41
|
+
- **Subtitles** — anonymous fetching for movies and TV (Arabic + more via TUI settings).
|
|
42
|
+
- **History & Favorites** — relaunch titles from a watch list or save them for later.
|
|
43
|
+
- **Settings menu** — configure player, download paths, quality, theme, and subtitles from the TUI.
|
|
44
|
+
- **17 color themes** — blue, red, purple, sunset, mint, coral, and more.
|
|
45
|
+
- **Discord Rich Presence** — now-playing status (optional, needs a client ID).
|
|
46
|
+
- **Headless-friendly** — numbered menu fallback for piped/SSH sessions.
|
|
47
|
+
- **Open to extensions** — drop a new provider file into `providers/` and it registers itself automatically.
|
|
48
|
+
|
|
49
|
+
## 📦 Requirements
|
|
50
|
+
|
|
51
|
+
- Python 3.11+
|
|
52
|
+
- A media player: **mpv** (default) or **vlc**
|
|
53
|
+
- `yt-dlp` + `ffmpeg` on your `PATH` for downloads
|
|
54
|
+
- Optional: `pypresence` for Discord Rich Presence
|
|
55
|
+
|
|
56
|
+
## 🚀 Installation
|
|
57
|
+
|
|
58
|
+
### PyPI (recommended)
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pipx install cinebox # cleanest: isolated tool install
|
|
62
|
+
uv tool install cinebox # same thing, uv-managed
|
|
63
|
+
pip install cinebox # or plain pip
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
> **`pipx`** keeps cinebox and its dependencies in an isolated environment
|
|
67
|
+
> so they never clash with your other Python packages. Install `pipx` with
|
|
68
|
+
> `pipx --help` → `pipx install pipx` if you don't have it yet.
|
|
69
|
+
|
|
70
|
+
### Local checkout (development)
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
git clone https://github.com/hosam00/cinebox
|
|
74
|
+
cd cinebox
|
|
75
|
+
uv sync --group dev # create .venv + install all deps
|
|
76
|
+
uv run cinebox --help # verify
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## 🎮 Quick start
|
|
80
|
+
|
|
81
|
+
### Interactive home screen (TUI)
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
cinebox
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Type a query to search, or use the home keys below. Everything else is driven
|
|
88
|
+
by arrow keys, `Enter`, `D` for download, `S` for settings.
|
|
89
|
+
|
|
90
|
+
### Stream directly by IMDb ID
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
cinebox play tt0133093 # The Matrix (1999)
|
|
94
|
+
cinebox play tt0944947 -t tv -s 1 -e 1 # GoT S01E01
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Download
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
cinebox download "the matrix" -q 720p
|
|
101
|
+
cinebox download "breaking bad" -t tv
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Headless search
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
echo 1 | cinebox search "inception" -t movie
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## ⌨️ Key bindings
|
|
111
|
+
|
|
112
|
+
| Key | Context | Action |
|
|
113
|
+
|-----------|----------------|------------------------------------------|
|
|
114
|
+
| `↑` / `↓` | All menus | Navigate |
|
|
115
|
+
| `Enter` | Results | Play selected |
|
|
116
|
+
| `Enter` | Quality menu | Watch |
|
|
117
|
+
| `D` | Quality menu | Download at highlighted quality |
|
|
118
|
+
| `D` | Episode picker | Download marked (or highlighted) |
|
|
119
|
+
| `Space` | Episode picker | Toggle mark for batch download |
|
|
120
|
+
| `F` | Search results | Toggle favorite |
|
|
121
|
+
| `b` | Any menu | Back |
|
|
122
|
+
| `q` | Any menu | Quit |
|
|
123
|
+
|
|
124
|
+
## ⚙️ Configuration
|
|
125
|
+
|
|
126
|
+
First run creates `~/.config/cinebox/config.toml`. Notable options:
|
|
127
|
+
|
|
128
|
+
```toml
|
|
129
|
+
[player]
|
|
130
|
+
default = "mpv" # mpv | vlc
|
|
131
|
+
fullscreen = true
|
|
132
|
+
extra_args = [] # extra args passed to the player (e.g. ["--mute"])
|
|
133
|
+
|
|
134
|
+
[download]
|
|
135
|
+
movies_dir = "~/Movies"
|
|
136
|
+
tv_dir = "~/Movies/TV"
|
|
137
|
+
quality = "1080p"
|
|
138
|
+
parallel_fragments = 3 # 1–16 concurrent download segments
|
|
139
|
+
|
|
140
|
+
[ui]
|
|
141
|
+
theme = "blue" # one of 17 themes
|
|
142
|
+
|
|
143
|
+
[discord]
|
|
144
|
+
enabled = false # requires a registered client ID
|
|
145
|
+
client_id = ""
|
|
146
|
+
|
|
147
|
+
[subtitle]
|
|
148
|
+
enabled = true
|
|
149
|
+
source = "auto" # auto | provider | yifysubtitles | subsource
|
|
150
|
+
default_lang = "en"
|
|
151
|
+
languages = ["en", "ar"]
|
|
152
|
+
auto_download = false # auto-fetch subs on watch (adds latency)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Any unset key falls back to its default. Everything above the `[discord]`
|
|
156
|
+
section is also editable from the TUI settings menu (`S`).
|
|
157
|
+
|
|
158
|
+
## 🔧 How it works
|
|
159
|
+
|
|
160
|
+
1. **Search** — `GET https://v3.sg.media-imdb.com/suggestion/{char}/{query}.json`
|
|
161
|
+
returns matching titles with IMDb IDs. No auth. Keyword-based providers
|
|
162
|
+
(e.g. Stardima) are merged automatically when present in the provider chain.
|
|
163
|
+
2. **Resolve** — providers turn an IMDb ID into a playable HLS/MP4 URL:
|
|
164
|
+
- `imdb_su` (primary) — follows the embed player chain to the `vaplayer.ru`
|
|
165
|
+
stream API; headers are spoofed so the CDN serves child manifests.
|
|
166
|
+
- `vidsrc` (best-effort) — `vidsrc.to` → `vsembed.ru` → vidsrc data API.
|
|
167
|
+
Encrypted results that need a browser are skipped.
|
|
168
|
+
- `multiembed` (best-effort) — currently Turnstile-gated and skipped.
|
|
169
|
+
Providers are **auto-discovered** when dropped into `src/cinebox/providers/`.
|
|
170
|
+
Set `priority` on your class to control chain order (lower runs first).
|
|
171
|
+
3. **Play** — mpv/VLC receive the URL plus the required `Referer`/`Origin`/
|
|
172
|
+
`User-Agent` headers, so the stream actually plays.
|
|
173
|
+
4. **Download** — the same resolved URL is handed to `yt-dlp` (optionally with
|
|
174
|
+
subtitle muxing) and saved under your configured movies/TV directories.
|
|
175
|
+
|
|
176
|
+
Providers are tried in order; if one fails, the next takes over. Some providers
|
|
177
|
+
can vanish or change overnight — resolution is best-effort by design.
|
|
178
|
+
|
|
179
|
+
## 🗂️ Project layout
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
src/cinebox/
|
|
183
|
+
├── __init__.py click CLI + TUI orchestration (search / play / download)
|
|
184
|
+
├── config.py TOML config: defaults, paths, endpoints
|
|
185
|
+
├── search.py IMDb suggestion endpoint + merged search_all()
|
|
186
|
+
├── models.py pydantic models (SearchResult, StreamInfo, MediaType)
|
|
187
|
+
├── metadata.py best-effort IMDb title-page enrichment (cached)
|
|
188
|
+
├── history.py watch history persistence
|
|
189
|
+
├── favorites.py favorites persistence
|
|
190
|
+
├── settings.py interactive settings catalog + editor
|
|
191
|
+
├── discord_rpc.py optional Discord Rich Presence
|
|
192
|
+
├── utils.py logging, HTTP client, retry helpers
|
|
193
|
+
├── player.py mpv / VLC launch command builders
|
|
194
|
+
├── downloader.py yt-dlp wrapper + quality mapping
|
|
195
|
+
├── subtitles.py anonymous subtitle fetch (movies + TV)
|
|
196
|
+
├── ui/
|
|
197
|
+
│ ├── themes.py 17 color themes
|
|
198
|
+
│ ├── art.py ASCII header + goodbye art
|
|
199
|
+
│ ├── terminal.py raw terminal I/O helpers
|
|
200
|
+
│ ├── components.py menus, loading, episode picker, error panel, layout
|
|
201
|
+
│ └── poster.py ANSI truecolor poster rendering (Pillow + numpy)
|
|
202
|
+
└── providers/
|
|
203
|
+
├── base.py BaseProvider, ProviderRegistry, ResolutionError
|
|
204
|
+
├── discovery.py auto-discovery of concrete providers
|
|
205
|
+
├── keyword.py KeywordProvider ABC for keyword-search platforms
|
|
206
|
+
├── imdb_su.py primary provider (vaplayer stream API)
|
|
207
|
+
├── vidsrc.py best-effort fallback
|
|
208
|
+
└── multiembed.py best-effort fallback (Turnstile-gated)
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## 🧪 Development
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
uv sync --group dev
|
|
215
|
+
uv run pytest # 126 hermetic tests, no network
|
|
216
|
+
uv run ruff check src tests
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
To run the tool headlessly against the live network while developing:
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
env HOME=/tmp/fakehome uv run cinebox search "the matrix" -t movie
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Adding a new provider
|
|
226
|
+
|
|
227
|
+
Drop a Python file into `src/cinebox/providers/` (e.g. `stardima.py`):
|
|
228
|
+
|
|
229
|
+
```python
|
|
230
|
+
from __future__ import annotations
|
|
231
|
+
from cinebox.providers.base import BaseProvider
|
|
232
|
+
from cinebox.models import MediaType, StreamInfo
|
|
233
|
+
|
|
234
|
+
class StardiMaProvider(BaseProvider):
|
|
235
|
+
name = "stardima"
|
|
236
|
+
priority = 40 # lower number = runs first in the chain
|
|
237
|
+
|
|
238
|
+
def resolve(self, imdb_id: str, media_type: MediaType,
|
|
239
|
+
season: int | None = None, episode: int | None = None) -> StreamInfo:
|
|
240
|
+
# ... fetch the stream URL from stardima ...
|
|
241
|
+
...
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
That's it — **no registry edits required**. `discovery.py` picks up any
|
|
245
|
+
concrete `BaseProvider` subclass in the package and adds it to the chain.
|
|
246
|
+
Set `priority` to control position (default `100`; `imdb_su` is `10`,
|
|
247
|
+
`vidsrc` `20`, `multiembed` `30`).
|
|
248
|
+
|
|
249
|
+
#### Keyword-search platforms (e.g. Stardima)
|
|
250
|
+
|
|
251
|
+
If your source resolves streams by raw keywords rather than IMDb IDs,
|
|
252
|
+
subclass `KeywordProvider` instead:
|
|
253
|
+
|
|
254
|
+
```python
|
|
255
|
+
from cinebox.providers.keyword import KeywordProvider
|
|
256
|
+
from cinebox.models import SearchResult
|
|
257
|
+
|
|
258
|
+
class StardiMaProvider(KeywordProvider):
|
|
259
|
+
scheme = "stardima"
|
|
260
|
+
priority = 40
|
|
261
|
+
|
|
262
|
+
def search(self, query, media_type=None):
|
|
263
|
+
# return list[SearchResult] — use self.synthetic_id("12345") for
|
|
264
|
+
# IDs the platform can't map back to IMDb
|
|
265
|
+
...
|
|
266
|
+
|
|
267
|
+
def resolve(self, imdb_id, media_type, season=None, episode=None):
|
|
268
|
+
# imdb_id may be synthetic: "stardima:12345"
|
|
269
|
+
...
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Keyword providers are merged into search results automatically alongside
|
|
273
|
+
IMDb suggestions. See `src/cinebox/providers/keyword.py` for the full interface.
|
|
274
|
+
|
|
275
|
+
## ✅ Roadmap
|
|
276
|
+
|
|
277
|
+
- [x] PyPI release via Trusted Publishing
|
|
278
|
+
- [x] Keyword-search provider interface (Stardima-ready)
|
|
279
|
+
- [x] Auto-discovery for drop-in providers
|
|
280
|
+
- [ ] `fzf` integration / fuzzy search over history & favorites
|
|
281
|
+
- [ ] More subtitle sources (incl. forced/embedded options)
|
|
282
|
+
- [ ] Provider config: per-provider enable/disable + health checks
|
|
283
|
+
- [ ] A full Stardima keyword provider (PR welcome)
|
|
284
|
+
|
|
285
|
+
## ⚠️ Disclaimer
|
|
286
|
+
|
|
287
|
+
For content you have the right to consume. The tool indexes publicly
|
|
288
|
+
accessible streams and hosts no media. Providers and their CDNs may change or
|
|
289
|
+
break at any time — use at your own risk, and always respect copyright.
|
|
290
|
+
|
|
291
|
+
Read the full [MIT license](LICENSE).
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
<p align="center">
|
|
296
|
+
<sub>Made with ❤️· Rich · yt-dlp · Python</sub>
|
|
297
|
+
</p>
|