plexo-cli 0.1.0__tar.gz → 0.1.2__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. plexo_cli-0.1.2/.gitignore +9 -0
  2. plexo_cli-0.1.2/PKG-INFO +231 -0
  3. plexo_cli-0.1.2/README.md +199 -0
  4. {plexo_cli-0.1.0 → plexo_cli-0.1.2}/pyproject.toml +1 -1
  5. {plexo_cli-0.1.0 → plexo_cli-0.1.2}/src/plexo/__init__.py +1 -1
  6. {plexo_cli-0.1.0 → plexo_cli-0.1.2}/src/plexo/cli.py +118 -11
  7. plexo_cli-0.1.2/src/plexo/core/client.py +304 -0
  8. plexo_cli-0.1.2/src/plexo/core/engine.py +476 -0
  9. plexo_cli-0.1.2/src/plexo/core/qos.py +125 -0
  10. {plexo_cli-0.1.0 → plexo_cli-0.1.2}/src/plexo/core/sparse.py +15 -6
  11. {plexo_cli-0.1.0 → plexo_cli-0.1.2}/src/plexo/torrent/client.py +26 -2
  12. {plexo_cli-0.1.0 → plexo_cli-0.1.2}/src/plexo/torrent/metadata.py +74 -17
  13. {plexo_cli-0.1.0 → plexo_cli-0.1.2}/src/plexo/ui/dashboard.py +19 -3
  14. plexo_cli-0.1.2/src/plexo/ui/watcher.py +311 -0
  15. plexo_cli-0.1.2/tests/test_engine.py +239 -0
  16. plexo_cli-0.1.2/tests/test_mirror.py +426 -0
  17. {plexo_cli-0.1.0 → plexo_cli-0.1.2}/tests/test_torrent.py +46 -0
  18. plexo_cli-0.1.2/tests/test_watcher.py +182 -0
  19. plexo_cli-0.1.0/.gitignore +0 -9
  20. plexo_cli-0.1.0/PKG-INFO +0 -91
  21. plexo_cli-0.1.0/README.md +0 -59
  22. plexo_cli-0.1.0/src/plexo/core/client.py +0 -148
  23. plexo_cli-0.1.0/src/plexo/core/engine.py +0 -266
  24. plexo_cli-0.1.0/src/plexo/core/qos.py +0 -76
  25. plexo_cli-0.1.0/tests/test_engine.py +0 -93
  26. {plexo_cli-0.1.0 → plexo_cli-0.1.2}/src/plexo/core/__init__.py +0 -0
  27. {plexo_cli-0.1.0 → plexo_cli-0.1.2}/src/plexo/core/adapter.py +0 -0
  28. {plexo_cli-0.1.0 → plexo_cli-0.1.2}/src/plexo/proxy/__init__.py +0 -0
  29. {plexo_cli-0.1.0 → plexo_cli-0.1.2}/src/plexo/proxy/server.py +0 -0
  30. {plexo_cli-0.1.0 → plexo_cli-0.1.2}/src/plexo/torrent/__init__.py +0 -0
  31. {plexo_cli-0.1.0 → plexo_cli-0.1.2}/src/plexo/torrent/bencode.py +0 -0
  32. {plexo_cli-0.1.0 → plexo_cli-0.1.2}/src/plexo/ui/__init__.py +0 -0
  33. {plexo_cli-0.1.0 → plexo_cli-0.1.2}/tests/__init__.py +0 -0
  34. {plexo_cli-0.1.0 → plexo_cli-0.1.2}/tests/test_adapter.py +0 -0
  35. {plexo_cli-0.1.0 → plexo_cli-0.1.2}/tests/test_sparse.py +0 -0
@@ -0,0 +1,9 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.pyo
4
+ *.pyd
5
+ .pytest_cache/
6
+ *.egg-info/
7
+ dist/
8
+ build/
9
+ .env
@@ -0,0 +1,231 @@
1
+ Metadata-Version: 2.5
2
+ Name: plexo-cli
3
+ Version: 0.1.2
4
+ Summary: Multi-network bandwidth bonding download engine & CLI
5
+ Author-email: Jagjot Singh <gotogether@gmail.com>
6
+ License-Expression: MIT
7
+ Keywords: bandwidth-aggregation,download-accelerator,multi-interface,network-bonding,proxy,torrent
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Environment :: Console
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Topic :: Internet
21
+ Classifier: Topic :: Utilities
22
+ Requires-Python: >=3.9
23
+ Requires-Dist: click>=8.1.0
24
+ Requires-Dist: httpx>=0.27.0
25
+ Requires-Dist: psutil>=5.9.0
26
+ Requires-Dist: rich>=13.7.0
27
+ Requires-Dist: urllib3>=2.0.0
28
+ Provides-Extra: test
29
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == 'test'
30
+ Requires-Dist: pytest>=8.0.0; extra == 'test'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # ⚡ Plexo CLI (`plexo-cli`)
34
+
35
+ [![PyPI version](https://img.shields.io/pypi/v/plexo-cli.svg?color=blue)](https://pypi.org/project/plexo-cli/)
36
+ [![Python versions](https://img.shields.io/pypi/pyversions/plexo-cli.svg)](https://pypi.org/project/plexo-cli/)
37
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
38
+ [![Tests](https://img.shields.io/badge/tests-36%20passed-success.svg)](tests/)
39
+
40
+ > **Universal, lightweight, terminal-first multi-network bandwidth bonding and torrent engine.**
41
+ > Combines Wi-Fi, Ethernet, and Mobile 5G tethering into one unified high-speed pipe directly from your command line.
42
+
43
+ ---
44
+
45
+ ## 🚀 Key Features
46
+
47
+ * **🌐 Multi-Socket Bandwidth Bonding:** Binds parallel download workers to distinct physical network interfaces using source-IP socket routing (`urllib3.PoolManager(source_address=(NIC_IP, 0))`).
48
+ * **🧲 BitTorrent Multi-Homing Swarm:** Slices BitTorrent peer swarms across multiple network adapters simultaneously with SHA-1 piece verification.
49
+ * **🎬 Watch While Downloading (`--sequential`):** Prioritizes file headers (moov container) and opening frames so media players like **VLC** can stream video immediately from byte 0.
50
+ * **🪞 Multi-Source Mirror Bonding (`-m / --mirror`):** Pulls chunks simultaneously across distinct server mirrors (e.g. Mirror A on Wi-Fi, Mirror B on 5G) to bypass server-side IP throttling.
51
+ * **🛡️ Per-Adapter Data Quotas (`-q / --quota`):** Protects mobile hotspot data caps (e.g. `-q usb=1.5GB`). When the cap is reached, that connection rests while unlimited Wi-Fi finishes the rest.
52
+ * **📂 Selective Torrent Downloads (`--list`, `--select`):** Inspect contents of multi-file season packs and download only the specific episodes you need.
53
+ * **📋 Background Clipboard Watcher (`plexo watch`):** IDM-style daemon that monitors the system clipboard and auto-captures `.mp4`, `.mkv`, `.zip`, `.iso`, or `magnet:` links.
54
+ * **⚡ Zero-Touch Local Acceleration Proxy (`plexo proxy`):** Runs a local forward proxy (`127.0.0.1:8899`) to bond traffic for `pip`, `curl`, `git`, and `huggingface-cli`.
55
+ * **💾 Smart Checkpointing & Instant Resume (`.plexo`):** Non-destructive NTFS/ext4 sparse file pre-allocation with zero-overhead resume on interrupted downloads.
56
+
57
+ ---
58
+
59
+ ## 📦 Installation
60
+
61
+ ### From PyPI
62
+ ```bash
63
+ pip install plexo-cli
64
+ ```
65
+
66
+ ### From Source (Editable Mode)
67
+ ```bash
68
+ git clone https://github.com/anmolkapil/plexo.git
69
+ cd plexo/packages/plexo-cli
70
+ pip install -e .
71
+ ```
72
+
73
+ ---
74
+
75
+ ## 🛠️ CLI Command Reference
76
+
77
+ ### 1. Discover Active Interfaces: `plexo adapters`
78
+ Scans and validates all active physical, Ethernet, and wireless interfaces, verifying outward internet reachability via canary probes:
79
+ ```bash
80
+ plexo adapters
81
+ ```
82
+
83
+ *Example Output:*
84
+ ```text
85
+ +-------------------+---------------+----------+---------+--------+
86
+ | Adapter Name | IP Address | Type | State | Canary |
87
+ +-------------------+---------------+----------+---------+--------+
88
+ | Wi-Fi (Hostel) | 192.168.1.104 | Wi-Fi | UP | ONLINE |
89
+ | Phone 5G USB | 192.168.43.55 | Ethernet | UP | ONLINE |
90
+ +-------------------+---------------+----------+---------+--------+
91
+ ```
92
+
93
+ ---
94
+
95
+ ### 2. Bonded HTTP Downloads: `plexo download`
96
+
97
+ #### Basic Parallel Download
98
+ ```bash
99
+ plexo download "https://example.com/largefile.zip" -o "largefile.zip"
100
+ ```
101
+
102
+ #### Watch While Downloading (`--sequential`)
103
+ Download chunks in strict chronological order so you can open and watch the video in VLC within 30 seconds:
104
+ ```bash
105
+ plexo download "https://example.com/movie.mp4" --sequential -o "movie.mp4"
106
+ ```
107
+
108
+ #### Multi-Source Mirror Bonding (`-m / --mirror`)
109
+ Distribute chunk downloads across multiple server mirrors and multiple adapters:
110
+ ```bash
111
+ plexo download "https://mirror1.com/ubuntu.iso" -m "https://mirror2.com/ubuntu.iso" -o "ubuntu.iso"
112
+ ```
113
+
114
+ #### Protect Mobile Data Caps (`-q / --quota`)
115
+ Specify a maximum data quota for any adapter by name or IP:
116
+ ```bash
117
+ # Cap Phone USB tethering to 1.5 GB, let Wi-Fi pull the rest:
118
+ plexo download "<url>" -q "usb=1.5GB"
119
+
120
+ # Cap a specific network IP to 500 MB:
121
+ plexo download "<url>" -q "192.168.43.55=500MB"
122
+ ```
123
+
124
+ #### Authenticated / Protected Filelockers (`--cookie`, `--referer`, `-H`)
125
+ ```bash
126
+ plexo download "<protected-url>" \
127
+ --cookie "session_token=abcdef123456" \
128
+ --referer "https://filelocker.com/view" \
129
+ -H "Authorization: Bearer my_token"
130
+ ```
131
+
132
+ ---
133
+
134
+ ### 3. BitTorrent Swarm Multi-Homing: `plexo torrent`
135
+
136
+ #### Magnet Links & `.torrent` Files
137
+ ```bash
138
+ # Magnet URI:
139
+ plexo torrent "magnet:?xt=urn:btih:EXAMPLE_HASH&dn=Debian" -o "D:\Downloads"
140
+
141
+ # Local .torrent file:
142
+ plexo torrent "debian.torrent" -o "D:\Downloads"
143
+ ```
144
+
145
+ #### Inspect Torrent Files (`--list`)
146
+ ```bash
147
+ plexo torrent "magnet:?xt=urn:btih:..." --list
148
+ ```
149
+
150
+ #### Download Selected Files (`--select`)
151
+ Download only specific files or episodes from large torrents:
152
+ ```bash
153
+ # Download only Episode 1 (index 1):
154
+ plexo torrent "magnet:?xt=urn:btih:..." --select 1
155
+
156
+ # Download multiple episodes:
157
+ plexo torrent "magnet:?xt=urn:btih:..." --select 1,2,5
158
+ ```
159
+
160
+ #### Sequential Torrent Playback (`--sequential`)
161
+ ```bash
162
+ plexo torrent "magnet:?xt=urn:btih:..." --sequential -o "D:\Movies"
163
+ ```
164
+
165
+ ---
166
+
167
+ ### 4. Background Clipboard Watcher: `plexo watch`
168
+ Listens in the background for copied download links (`.mp4`, `.mkv`, `.zip`, `.iso`, `magnet:`, etc.):
169
+
170
+ ```bash
171
+ # Interactive mode (prompts [Y/n] when a downloadable URL is copied):
172
+ plexo watch
173
+
174
+ # Headless / Auto-download mode:
175
+ plexo watch -a -o "D:\Downloads"
176
+
177
+ # Auto-download with sequential streaming:
178
+ plexo watch -a --sequential -o "D:\Movies"
179
+ ```
180
+
181
+ ---
182
+
183
+ ### 5. Local Acceleration Proxy: `plexo proxy`
184
+ Accelerate development tools across bonded network connections:
185
+
186
+ ```bash
187
+ # Start proxy on default port 8899:
188
+ plexo proxy --port 8899
189
+ ```
190
+
191
+ In another terminal, direct tools to use the proxy:
192
+ ```bash
193
+ # Accelerate pip:
194
+ pip install torch torchvision --proxy http://127.0.0.1:8899
195
+
196
+ # Accelerate curl / Hugging Face CLI:
197
+ export HTTPS_PROXY="http://127.0.0.1:8899"
198
+ curl -O https://example.com/dataset.tar.gz
199
+ ```
200
+
201
+ ---
202
+
203
+ ### 6. Probe Server Capabilities: `plexo info`
204
+ Inspect remote server headers, file size, MIME type, and byte-range support:
205
+ ```bash
206
+ plexo info "https://example.com/file.zip"
207
+ ```
208
+
209
+ ---
210
+
211
+ ## 🧪 Verification & Testing
212
+
213
+ The package includes a comprehensive automated test suite testing live sockets, mock mirrors, sparse file allocation, torrent bencoding, QoS quota enforcement, and clipboard regex extractors:
214
+
215
+ ```bash
216
+ pytest packages/plexo-cli -v
217
+ ```
218
+ ```text
219
+ tests/test_adapter.py PASSED (3/3)
220
+ tests/test_sparse.py PASSED (3/3)
221
+ tests/test_torrent.py PASSED (4/4)
222
+ tests/test_engine.py PASSED (5/5)
223
+ tests/test_watcher.py PASSED (12/12)
224
+ tests/test_mirror.py PASSED (9/9)
225
+ ============================= 36 passed in 10.10s =============================
226
+ ```
227
+
228
+ ---
229
+
230
+ ## 📄 License
231
+ MIT © Anmol Kapil & Jagjot Singh
@@ -0,0 +1,199 @@
1
+ # ⚡ Plexo CLI (`plexo-cli`)
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/plexo-cli.svg?color=blue)](https://pypi.org/project/plexo-cli/)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/plexo-cli.svg)](https://pypi.org/project/plexo-cli/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
6
+ [![Tests](https://img.shields.io/badge/tests-36%20passed-success.svg)](tests/)
7
+
8
+ > **Universal, lightweight, terminal-first multi-network bandwidth bonding and torrent engine.**
9
+ > Combines Wi-Fi, Ethernet, and Mobile 5G tethering into one unified high-speed pipe directly from your command line.
10
+
11
+ ---
12
+
13
+ ## 🚀 Key Features
14
+
15
+ * **🌐 Multi-Socket Bandwidth Bonding:** Binds parallel download workers to distinct physical network interfaces using source-IP socket routing (`urllib3.PoolManager(source_address=(NIC_IP, 0))`).
16
+ * **🧲 BitTorrent Multi-Homing Swarm:** Slices BitTorrent peer swarms across multiple network adapters simultaneously with SHA-1 piece verification.
17
+ * **🎬 Watch While Downloading (`--sequential`):** Prioritizes file headers (moov container) and opening frames so media players like **VLC** can stream video immediately from byte 0.
18
+ * **🪞 Multi-Source Mirror Bonding (`-m / --mirror`):** Pulls chunks simultaneously across distinct server mirrors (e.g. Mirror A on Wi-Fi, Mirror B on 5G) to bypass server-side IP throttling.
19
+ * **🛡️ Per-Adapter Data Quotas (`-q / --quota`):** Protects mobile hotspot data caps (e.g. `-q usb=1.5GB`). When the cap is reached, that connection rests while unlimited Wi-Fi finishes the rest.
20
+ * **📂 Selective Torrent Downloads (`--list`, `--select`):** Inspect contents of multi-file season packs and download only the specific episodes you need.
21
+ * **📋 Background Clipboard Watcher (`plexo watch`):** IDM-style daemon that monitors the system clipboard and auto-captures `.mp4`, `.mkv`, `.zip`, `.iso`, or `magnet:` links.
22
+ * **⚡ Zero-Touch Local Acceleration Proxy (`plexo proxy`):** Runs a local forward proxy (`127.0.0.1:8899`) to bond traffic for `pip`, `curl`, `git`, and `huggingface-cli`.
23
+ * **💾 Smart Checkpointing & Instant Resume (`.plexo`):** Non-destructive NTFS/ext4 sparse file pre-allocation with zero-overhead resume on interrupted downloads.
24
+
25
+ ---
26
+
27
+ ## 📦 Installation
28
+
29
+ ### From PyPI
30
+ ```bash
31
+ pip install plexo-cli
32
+ ```
33
+
34
+ ### From Source (Editable Mode)
35
+ ```bash
36
+ git clone https://github.com/anmolkapil/plexo.git
37
+ cd plexo/packages/plexo-cli
38
+ pip install -e .
39
+ ```
40
+
41
+ ---
42
+
43
+ ## 🛠️ CLI Command Reference
44
+
45
+ ### 1. Discover Active Interfaces: `plexo adapters`
46
+ Scans and validates all active physical, Ethernet, and wireless interfaces, verifying outward internet reachability via canary probes:
47
+ ```bash
48
+ plexo adapters
49
+ ```
50
+
51
+ *Example Output:*
52
+ ```text
53
+ +-------------------+---------------+----------+---------+--------+
54
+ | Adapter Name | IP Address | Type | State | Canary |
55
+ +-------------------+---------------+----------+---------+--------+
56
+ | Wi-Fi (Hostel) | 192.168.1.104 | Wi-Fi | UP | ONLINE |
57
+ | Phone 5G USB | 192.168.43.55 | Ethernet | UP | ONLINE |
58
+ +-------------------+---------------+----------+---------+--------+
59
+ ```
60
+
61
+ ---
62
+
63
+ ### 2. Bonded HTTP Downloads: `plexo download`
64
+
65
+ #### Basic Parallel Download
66
+ ```bash
67
+ plexo download "https://example.com/largefile.zip" -o "largefile.zip"
68
+ ```
69
+
70
+ #### Watch While Downloading (`--sequential`)
71
+ Download chunks in strict chronological order so you can open and watch the video in VLC within 30 seconds:
72
+ ```bash
73
+ plexo download "https://example.com/movie.mp4" --sequential -o "movie.mp4"
74
+ ```
75
+
76
+ #### Multi-Source Mirror Bonding (`-m / --mirror`)
77
+ Distribute chunk downloads across multiple server mirrors and multiple adapters:
78
+ ```bash
79
+ plexo download "https://mirror1.com/ubuntu.iso" -m "https://mirror2.com/ubuntu.iso" -o "ubuntu.iso"
80
+ ```
81
+
82
+ #### Protect Mobile Data Caps (`-q / --quota`)
83
+ Specify a maximum data quota for any adapter by name or IP:
84
+ ```bash
85
+ # Cap Phone USB tethering to 1.5 GB, let Wi-Fi pull the rest:
86
+ plexo download "<url>" -q "usb=1.5GB"
87
+
88
+ # Cap a specific network IP to 500 MB:
89
+ plexo download "<url>" -q "192.168.43.55=500MB"
90
+ ```
91
+
92
+ #### Authenticated / Protected Filelockers (`--cookie`, `--referer`, `-H`)
93
+ ```bash
94
+ plexo download "<protected-url>" \
95
+ --cookie "session_token=abcdef123456" \
96
+ --referer "https://filelocker.com/view" \
97
+ -H "Authorization: Bearer my_token"
98
+ ```
99
+
100
+ ---
101
+
102
+ ### 3. BitTorrent Swarm Multi-Homing: `plexo torrent`
103
+
104
+ #### Magnet Links & `.torrent` Files
105
+ ```bash
106
+ # Magnet URI:
107
+ plexo torrent "magnet:?xt=urn:btih:EXAMPLE_HASH&dn=Debian" -o "D:\Downloads"
108
+
109
+ # Local .torrent file:
110
+ plexo torrent "debian.torrent" -o "D:\Downloads"
111
+ ```
112
+
113
+ #### Inspect Torrent Files (`--list`)
114
+ ```bash
115
+ plexo torrent "magnet:?xt=urn:btih:..." --list
116
+ ```
117
+
118
+ #### Download Selected Files (`--select`)
119
+ Download only specific files or episodes from large torrents:
120
+ ```bash
121
+ # Download only Episode 1 (index 1):
122
+ plexo torrent "magnet:?xt=urn:btih:..." --select 1
123
+
124
+ # Download multiple episodes:
125
+ plexo torrent "magnet:?xt=urn:btih:..." --select 1,2,5
126
+ ```
127
+
128
+ #### Sequential Torrent Playback (`--sequential`)
129
+ ```bash
130
+ plexo torrent "magnet:?xt=urn:btih:..." --sequential -o "D:\Movies"
131
+ ```
132
+
133
+ ---
134
+
135
+ ### 4. Background Clipboard Watcher: `plexo watch`
136
+ Listens in the background for copied download links (`.mp4`, `.mkv`, `.zip`, `.iso`, `magnet:`, etc.):
137
+
138
+ ```bash
139
+ # Interactive mode (prompts [Y/n] when a downloadable URL is copied):
140
+ plexo watch
141
+
142
+ # Headless / Auto-download mode:
143
+ plexo watch -a -o "D:\Downloads"
144
+
145
+ # Auto-download with sequential streaming:
146
+ plexo watch -a --sequential -o "D:\Movies"
147
+ ```
148
+
149
+ ---
150
+
151
+ ### 5. Local Acceleration Proxy: `plexo proxy`
152
+ Accelerate development tools across bonded network connections:
153
+
154
+ ```bash
155
+ # Start proxy on default port 8899:
156
+ plexo proxy --port 8899
157
+ ```
158
+
159
+ In another terminal, direct tools to use the proxy:
160
+ ```bash
161
+ # Accelerate pip:
162
+ pip install torch torchvision --proxy http://127.0.0.1:8899
163
+
164
+ # Accelerate curl / Hugging Face CLI:
165
+ export HTTPS_PROXY="http://127.0.0.1:8899"
166
+ curl -O https://example.com/dataset.tar.gz
167
+ ```
168
+
169
+ ---
170
+
171
+ ### 6. Probe Server Capabilities: `plexo info`
172
+ Inspect remote server headers, file size, MIME type, and byte-range support:
173
+ ```bash
174
+ plexo info "https://example.com/file.zip"
175
+ ```
176
+
177
+ ---
178
+
179
+ ## 🧪 Verification & Testing
180
+
181
+ The package includes a comprehensive automated test suite testing live sockets, mock mirrors, sparse file allocation, torrent bencoding, QoS quota enforcement, and clipboard regex extractors:
182
+
183
+ ```bash
184
+ pytest packages/plexo-cli -v
185
+ ```
186
+ ```text
187
+ tests/test_adapter.py PASSED (3/3)
188
+ tests/test_sparse.py PASSED (3/3)
189
+ tests/test_torrent.py PASSED (4/4)
190
+ tests/test_engine.py PASSED (5/5)
191
+ tests/test_watcher.py PASSED (12/12)
192
+ tests/test_mirror.py PASSED (9/9)
193
+ ============================= 36 passed in 10.10s =============================
194
+ ```
195
+
196
+ ---
197
+
198
+ ## 📄 License
199
+ MIT © Anmol Kapil & Jagjot Singh
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "plexo-cli"
7
- version = "0.1.0"
7
+ version = "0.1.2"
8
8
  description = "Multi-network bandwidth bonding download engine & CLI"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -1,4 +1,4 @@
1
1
  """Plexo CLI: Multi-network bandwidth bonding download accelerator."""
2
2
 
3
- __version__ = "0.1.0"
3
+ __version__ = "0.1.2"
4
4
  __author__ = "Jagjot Singh"
@@ -1,6 +1,6 @@
1
1
  """Command Line Interface for Plexo.
2
2
 
3
- Exposes subcommands: download, adapters, proxy, info.
3
+ Exposes subcommands: download, adapters, proxy, torrent, watch, info.
4
4
  """
5
5
 
6
6
  from __future__ import annotations
@@ -76,16 +76,56 @@ def list_adapters(show_all: bool):
76
76
 
77
77
  @main.command(name="download")
78
78
  @click.argument("url")
79
+ @click.option("-m", "--mirror", "mirrors", multiple=True, help="Alternative mirror URL(s) for the same file.")
79
80
  @click.option("-o", "--output", "output_path", type=click.Path(), default=None, help="Output destination path.")
80
81
  @click.option("--chunk-size", default=8, type=int, help="Chunk size in MB (default: 8 MB).")
81
82
  @click.option("--workers", default=2, type=int, help="Workers per network adapter (default: 2).")
82
- def download_file(url: str, output_path: Optional[str], chunk_size: int, workers: int):
83
- """Download a file using bonded multi-network connections."""
83
+ @click.option("-H", "--header", "headers_list", multiple=True, help="Custom HTTP header (e.g. -H 'Auth: token').")
84
+ @click.option("--cookie", default=None, help="Custom Cookie header for protected sites/filelockers.")
85
+ @click.option("--referer", default=None, help="Custom Referer URL for sites requiring origin verification.")
86
+ @click.option("--sequential", is_flag=True, help="Download in sequential order (watch media while downloading).")
87
+ @click.option("-q", "--quota", "quota_list", multiple=True, help="Data cap for adapter (e.g. -q usb=1.5GB).")
88
+ def download_file(
89
+ url: str,
90
+ mirrors: tuple[str, ...],
91
+ output_path: Optional[str],
92
+ chunk_size: int,
93
+ workers: int,
94
+ headers_list: tuple[str, ...],
95
+ cookie: Optional[str],
96
+ referer: Optional[str],
97
+ sequential: bool,
98
+ quota_list: tuple[str, ...],
99
+ ):
100
+ """Download a file using bonded multi-network connections and mirrors."""
101
+ from .core.qos import parse_size_str
84
102
  adapters = get_bonded_pool()
85
103
  if not adapters:
86
104
  console.print("[bold red]Error: No active online network adapters detected.[/bold red]")
87
105
  raise click.Abort()
88
106
 
107
+ # Parse custom headers
108
+ parsed_headers = {}
109
+ for h in headers_list:
110
+ if ":" in h:
111
+ k, v = h.split(":", 1)
112
+ parsed_headers[k.strip()] = v.strip()
113
+
114
+ # Parse adapter data quotas
115
+ parsed_quotas = {}
116
+ for q in quota_list:
117
+ if "=" in q:
118
+ ident, s_val = q.split("=", 1)
119
+ parsed_quotas[ident.strip()] = parse_size_str(s_val.strip())
120
+
121
+ # Parse alternative mirror URLs
122
+ parsed_mirrors = []
123
+ for m in mirrors:
124
+ for sub_m in m.split(","):
125
+ sub_m = sub_m.strip()
126
+ if sub_m and sub_m not in parsed_mirrors and sub_m != url:
127
+ parsed_mirrors.append(sub_m)
128
+
89
129
  dashboard = TerminalDashboard(console=console)
90
130
  chunk_bytes = chunk_size * 1024 * 1024
91
131
 
@@ -95,10 +135,16 @@ def download_file(url: str, output_path: Optional[str], chunk_size: int, workers
95
135
  adapters=adapters,
96
136
  chunk_size=chunk_bytes,
97
137
  workers_per_adapter=workers,
138
+ custom_headers=parsed_headers,
139
+ cookie=cookie,
140
+ referer=referer,
141
+ sequential=sequential,
142
+ quotas=parsed_quotas,
143
+ mirrors=parsed_mirrors,
98
144
  )
99
145
 
100
146
  probe = engine._prepare()
101
- dashboard.setup(probe.filename, probe.total_bytes, adapters)
147
+ dashboard.setup(probe.filename, probe.total_bytes, adapters, mirrors=engine.mirrors)
102
148
 
103
149
  start_time = time.time()
104
150
  with dashboard.progress:
@@ -112,7 +158,7 @@ def download_file(url: str, output_path: Optional[str], chunk_size: int, workers
112
158
  with SparseFileWriter(final_path, total_bytes) as writer:
113
159
  checksum = writer.calculate_checksum()
114
160
 
115
- dashboard.finish(final_path.name, total_bytes, elapsed, checksum)
161
+ dashboard.finish(final_path.name, total_bytes, elapsed, checksum, mirrors=engine.mirrors)
116
162
 
117
163
 
118
164
  @main.command(name="proxy")
@@ -126,26 +172,64 @@ def start_proxy(host: str, port: int):
126
172
  @main.command(name="torrent")
127
173
  @click.argument("source")
128
174
  @click.option("-o", "--output-dir", default=".", help="Directory to save downloaded files.")
129
- def download_torrent(source: str, output_dir: str):
175
+ @click.option("--sequential", is_flag=True, help="Download pieces in sequential order (watch media while downloading).")
176
+ @click.option("--list", "list_files", is_flag=True, help="List all files in the torrent and exit.")
177
+ @click.option("--select", "select_files", default=None, help="Download only specific file numbers (e.g. --select 1,2).")
178
+ @click.option("-q", "--quota", "quota_list", multiple=True, help="Data cap for adapter (e.g. -q usb=1.5GB).")
179
+ def download_torrent(
180
+ source: str,
181
+ output_dir: str,
182
+ sequential: bool,
183
+ list_files: bool,
184
+ select_files: Optional[str],
185
+ quota_list: tuple[str, ...],
186
+ ):
130
187
  """Download a torrent or magnet link across bonded network adapters."""
188
+ from .core.qos import parse_size_str
131
189
  from .torrent.metadata import parse_magnet, parse_torrent_file
132
190
  from .torrent.client import TorrentBondingEngine
133
191
 
192
+ if source.startswith("magnet:?"):
193
+ meta = parse_magnet(source)
194
+ else:
195
+ meta = parse_torrent_file(source)
196
+
197
+ if list_files:
198
+ table = Table(title=f"[bold green]Torrent Contents: {meta.name}[/bold green]")
199
+ table.add_column("#", style="bold yellow", justify="right")
200
+ table.add_column("File Name / Path", style="cyan")
201
+ table.add_column("Size", style="magenta", justify="right")
202
+ for f in meta.files:
203
+ size_str = f"{f.size_mb:.2f} MB" if f.size_mb < 1024 else f"{f.size_gb:.2f} GB"
204
+ table.add_row(str(f.index), f.path, size_str)
205
+ console.print(table)
206
+ console.print(
207
+ "\n[dim]To download specific files, run:[/dim]\n"
208
+ f" plexo torrent \"{source}\" --select 1,2 -o \"{output_dir}\"\n"
209
+ )
210
+ return
211
+
134
212
  adapters = get_bonded_pool()
135
213
  if not adapters:
136
214
  console.print("[bold red]Error: No active online network adapters detected.[/bold red]")
137
215
  raise click.Abort()
138
216
 
139
- if source.startswith("magnet:?"):
140
- meta = parse_magnet(source)
141
- else:
142
- meta = parse_torrent_file(source)
217
+ selected_indices = None
218
+ if select_files:
219
+ selected_indices = [int(s.strip()) for s in select_files.split(",") if s.strip().isdigit()]
220
+
221
+ parsed_quotas = {}
222
+ for q in quota_list:
223
+ if "=" in q:
224
+ ident, s_val = q.split("=", 1)
225
+ parsed_quotas[ident.strip()] = parse_size_str(s_val.strip())
143
226
 
144
227
  console.print(
145
228
  Panel.fit(
146
229
  f"[bold cyan]Plexo Torrent Swarm Engine[/bold cyan]\n"
147
230
  f"Name: [bold yellow]{meta.name}[/bold yellow]\n"
148
231
  f"Info Hash: [dim]{meta.info_hash_hex}[/dim]\n"
232
+ f"Selected Files: [green]{len(selected_indices) if selected_indices else 'All'}[/green] / {len(meta.files)}\n"
149
233
  f"Active Network Links: [bold green]{len(adapters)}[/bold green]",
150
234
  border_style="cyan",
151
235
  )
@@ -154,7 +238,14 @@ def download_torrent(source: str, output_dir: str):
154
238
  dashboard = TerminalDashboard(console=console)
155
239
  dashboard.setup(meta.name, meta.total_length, adapters)
156
240
 
157
- engine = TorrentBondingEngine(metadata=meta, output_dir=output_dir, adapters=adapters)
241
+ engine = TorrentBondingEngine(
242
+ metadata=meta,
243
+ output_dir=output_dir,
244
+ adapters=adapters,
245
+ sequential=sequential,
246
+ selected_files=selected_indices,
247
+ quotas=parsed_quotas,
248
+ )
158
249
 
159
250
  start_time = time.time()
160
251
  with dashboard.progress:
@@ -170,6 +261,22 @@ def download_torrent(source: str, output_dir: str):
170
261
  dashboard.finish(final_path.name, total_bytes, elapsed, checksum)
171
262
 
172
263
 
264
+ @main.command(name="watch")
265
+ @click.option("--auto-download", "-y", is_flag=True, help="Automatically download detected links without prompting.")
266
+ @click.option("-o", "--output-dir", default=None, help="Directory to save downloaded files.")
267
+ @click.option("-i", "--poll-interval", default=1.0, type=float, help="Clipboard polling interval in seconds (default: 1.0).")
268
+ def watch_clipboard(auto_download: bool, output_dir: Optional[str], poll_interval: float):
269
+ """Monitor clipboard for download URLs and magnet links."""
270
+ from .ui.watcher import ClipboardWatcher
271
+ watcher = ClipboardWatcher(
272
+ poll_interval=poll_interval,
273
+ auto_download=auto_download,
274
+ output_dir=output_dir,
275
+ console=console,
276
+ )
277
+ watcher.run()
278
+
279
+
173
280
  @main.command(name="info")
174
281
  def show_info():
175
282
  """Display Plexo environment and bonding capabilities."""