pyackett 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.
Files changed (35) hide show
  1. pyackett-0.1.0/.github/workflows/publish.yml +21 -0
  2. pyackett-0.1.0/.github/workflows/test.yml +23 -0
  3. pyackett-0.1.0/.gitignore +8 -0
  4. pyackett-0.1.0/PKG-INFO +182 -0
  5. pyackett-0.1.0/README.md +152 -0
  6. pyackett-0.1.0/pyproject.toml +57 -0
  7. pyackett-0.1.0/src/pyackett/__init__.py +8 -0
  8. pyackett-0.1.0/src/pyackett/api/__init__.py +0 -0
  9. pyackett-0.1.0/src/pyackett/api/torznab.py +268 -0
  10. pyackett-0.1.0/src/pyackett/cli.py +100 -0
  11. pyackett-0.1.0/src/pyackett/core/__init__.py +0 -0
  12. pyackett-0.1.0/src/pyackett/core/cache.py +56 -0
  13. pyackett-0.1.0/src/pyackett/core/categories.py +109 -0
  14. pyackett-0.1.0/src/pyackett/core/definitions_fetcher.py +189 -0
  15. pyackett-0.1.0/src/pyackett/core/http.py +522 -0
  16. pyackett-0.1.0/src/pyackett/core/manager.py +216 -0
  17. pyackett-0.1.0/src/pyackett/core/models.py +240 -0
  18. pyackett-0.1.0/src/pyackett/definitions/iptorrents.yml +209 -0
  19. pyackett-0.1.0/src/pyackett/engine/__init__.py +0 -0
  20. pyackett-0.1.0/src/pyackett/engine/cardigann.py +878 -0
  21. pyackett-0.1.0/src/pyackett/engine/filters.py +274 -0
  22. pyackett-0.1.0/src/pyackett/engine/selectors.py +205 -0
  23. pyackett-0.1.0/src/pyackett/engine/template.py +301 -0
  24. pyackett-0.1.0/src/pyackett/pyackett.py +217 -0
  25. pyackett-0.1.0/src/pyackett/server/__init__.py +0 -0
  26. pyackett-0.1.0/src/pyackett/server/app.py +887 -0
  27. pyackett-0.1.0/tests/__init__.py +0 -0
  28. pyackett-0.1.0/tests/test_definitions_fetcher.py +98 -0
  29. pyackett-0.1.0/tests/test_filters.py +100 -0
  30. pyackett-0.1.0/tests/test_models.py +49 -0
  31. pyackett-0.1.0/tests/test_selectors.py +80 -0
  32. pyackett-0.1.0/tests/test_template.py +196 -0
  33. pyackett-0.1.0/tests/test_torznab_api.py +75 -0
  34. pyackett-0.1.0/tests/test_unicode_regex.py +35 -0
  35. pyackett-0.1.0/uv.lock +1834 -0
@@ -0,0 +1,21 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ id-token: write
9
+
10
+ jobs:
11
+ publish:
12
+ runs-on: ubuntu-latest
13
+ environment: pypi
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - uses: astral-sh/setup-uv@v5
18
+
19
+ - run: uv build
20
+
21
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,23 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ matrix:
13
+ python-version: ["3.12", "3.13"]
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - uses: astral-sh/setup-uv@v5
18
+
19
+ - run: uv python install ${{ matrix.python-version }}
20
+
21
+ - run: uv sync --python ${{ matrix.python-version }}
22
+
23
+ - run: uv run pytest tests/ -v
@@ -0,0 +1,8 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ *.egg-info/
5
+ .pytest_cache/
6
+ dist/
7
+ build/
8
+ *.pkl
@@ -0,0 +1,182 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyackett
3
+ Version: 0.1.0
4
+ Summary: Python clean room implementation of a Torznab-compatible indexer proxy
5
+ Project-URL: Homepage, https://github.com/dmarkey/pyackett
6
+ Project-URL: Repository, https://github.com/dmarkey/pyackett
7
+ Project-URL: Issues, https://github.com/dmarkey/pyackett/issues
8
+ Author: David Markey
9
+ License-Expression: MIT
10
+ Keywords: indexer,jackett,proxy,radarr,sonarr,torrent,torznab
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
16
+ Requires-Python: >=3.12
17
+ Requires-Dist: beautifulsoup4>=4.12.0
18
+ Requires-Dist: cachetools>=5.5.0
19
+ Requires-Dist: curl-cffi>=0.7.0
20
+ Requires-Dist: fastapi>=0.115.0
21
+ Requires-Dist: jsonpath-ng>=1.6.0
22
+ Requires-Dist: lxml>=5.0.0
23
+ Requires-Dist: pydantic>=2.0.0
24
+ Requires-Dist: python-dateutil>=2.9.0
25
+ Requires-Dist: pyyaml>=6.0
26
+ Requires-Dist: uvicorn[standard]>=0.32.0
27
+ Provides-Extra: cloudflare
28
+ Requires-Dist: camoufox[geoip]>=0.4.11; extra == 'cloudflare'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # Pyackett
32
+
33
+ A Python clean room implementation of a Torznab-compatible indexer proxy. Works as a standalone server (Sonarr/Radarr compatible) and as an importable Python library.
34
+
35
+ Loads Jackett's 550+ YAML indexer definitions directly. Includes browser-grade TLS fingerprinting and automatic Cloudflare Turnstile bypass.
36
+
37
+ ## Install
38
+
39
+ ```bash
40
+ pip install pyackett
41
+
42
+ # With Cloudflare challenge support (adds Camoufox browser):
43
+ pip install pyackett[cloudflare]
44
+ ```
45
+
46
+ ## Quick Start
47
+
48
+ ### As a server
49
+
50
+ ```bash
51
+ # Download definitions from Jackett GitHub and start the server
52
+ pyackett --from-github jackett --port 9117
53
+
54
+ # With a SOCKS5 proxy
55
+ pyackett --from-github jackett --proxy socks5://user:pass@host:1080
56
+
57
+ # With local definitions
58
+ pyackett -d /path/to/definitions
59
+ ```
60
+
61
+ Then point Sonarr/Radarr at `http://localhost:9117/api/v2.0/indexers/{id}/results/torznab/` with the API key shown in the web UI.
62
+
63
+ ### As a Python library
64
+
65
+ ```python
66
+ import asyncio
67
+ from pyackett import Pyackett
68
+
69
+ async def main():
70
+ pk = Pyackett(proxy="socks5://user:pass@host:1080")
71
+ pk.load_definitions_from_github(source="jackett")
72
+
73
+ await pk.configure_indexer("1337x", {})
74
+ await pk.configure_indexer("therarbg", {})
75
+
76
+ results = await pk.search("breaking bad", categories=[5000])
77
+ for r in results:
78
+ print(f"{r.title} | S:{r.seeders} | {r.size}")
79
+
80
+ await pk.close()
81
+
82
+ asyncio.run(main())
83
+ ```
84
+
85
+ ```python
86
+ # Synchronous wrapper for simple scripts
87
+ pk = Pyackett()
88
+ pk.load_definitions_from_github()
89
+ pk.configure_indexer("therarbg", {})
90
+ results = pk.search_sync("ubuntu")
91
+ ```
92
+
93
+ ## Features
94
+
95
+ ### Torznab API
96
+ - Full Torznab XML API compatible with Sonarr, Radarr, Prowlarr, qBittorrent
97
+ - TorrentPotato JSON support
98
+ - Capabilities endpoint per indexer
99
+ - Multi-indexer search (`/indexers/all/results/torznab/`)
100
+
101
+ ### Cardigann YAML Engine
102
+ - Loads all 550+ Jackett YAML indexer definitions
103
+ - Go-style template engine (`{{ if }}`, `{{ range }}`, `{{ eq }}`, etc.)
104
+ - CSS selector extraction (BeautifulSoup) and JSONPath
105
+ - 25+ filter functions (replace, dateparse, fuzzytime, regexp, etc.)
106
+ - 6 login methods (form, cookie, header/API key, GET, POST, captcha)
107
+
108
+ ### HTTP Client
109
+ - **curl_cffi** with Chrome/Firefox TLS fingerprint impersonation
110
+ - Built-in SOCKS5/SOCKS4/HTTP proxy support
111
+ - Request delay per indexer definition
112
+ - Automatic Cloudflare detection and bypass
113
+
114
+ ### Cloudflare Bypass (optional)
115
+ - Automatic detection of Cloudflare 403/503 challenges
116
+ - **Camoufox** (anti-detect Firefox) solves Turnstile challenges headlessly
117
+ - Local HTTP CONNECT proxy bridges SOCKS5 auth for browsers
118
+ - Firefox TLS fingerprint matching for cf_clearance cookie reuse
119
+ - Cookies cached per domain for the session
120
+ - Graceful fallback when Camoufox not installed (logs warning, skips CF sites)
121
+
122
+ ### Web UI
123
+ - Bootstrap 5 dashboard at `http://localhost:9117/`
124
+ - Browse and filter all 550+ available indexers
125
+ - Dynamic configuration forms generated from YAML settings
126
+ - Manual search with results table
127
+ - Per-indexer Torznab URL copy, edit, test, delete
128
+
129
+ ## CLI Options
130
+
131
+ ```
132
+ pyackett [OPTIONS]
133
+
134
+ --host HOST Bind address (default: 0.0.0.0)
135
+ -p, --port PORT Port (default: 9117)
136
+ --config-dir DIR Config/cache directory
137
+ -d, --definitions-dir DIR Local YAML definitions directory
138
+ --from-github {jackett,prowlarr}
139
+ Download definitions from GitHub
140
+ --branch BRANCH GitHub branch (default: master)
141
+ --update-definitions Force re-download definitions
142
+ --proxy URL Proxy (socks5://host:port, http://host:port)
143
+ --api-key KEY API key (auto-generated if not set)
144
+ --log-level {DEBUG,INFO,WARNING,ERROR}
145
+ ```
146
+
147
+ ## Architecture
148
+
149
+ ```
150
+ pyackett/
151
+ core/
152
+ models.py - ReleaseInfo, TorznabQuery, IndexerDefinition
153
+ manager.py - IndexerManager (load, configure, concurrent search)
154
+ http.py - curl_cffi client + Camoufox CF solver
155
+ cache.py - TTL result cache
156
+ categories.py - Torznab category mappings
157
+ definitions_fetcher.py - GitHub tarball downloader
158
+ engine/
159
+ cardigann.py - YAML definition interpreter
160
+ template.py - Go-style template engine
161
+ filters.py - 25+ data transformation filters
162
+ selectors.py - CSS + JSONPath extraction
163
+ api/
164
+ torznab.py - Torznab XML generation + query parsing
165
+ server/
166
+ app.py - FastAPI server + web UI
167
+ pyackett.py - Public library API
168
+ cli.py - CLI entry point
169
+ ```
170
+
171
+ ## Development
172
+
173
+ ```bash
174
+ git clone <repo>
175
+ cd pyackett
176
+ uv sync
177
+ uv run pytest tests/ -v
178
+ ```
179
+
180
+ ## License
181
+
182
+ MIT
@@ -0,0 +1,152 @@
1
+ # Pyackett
2
+
3
+ A Python clean room implementation of a Torznab-compatible indexer proxy. Works as a standalone server (Sonarr/Radarr compatible) and as an importable Python library.
4
+
5
+ Loads Jackett's 550+ YAML indexer definitions directly. Includes browser-grade TLS fingerprinting and automatic Cloudflare Turnstile bypass.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pip install pyackett
11
+
12
+ # With Cloudflare challenge support (adds Camoufox browser):
13
+ pip install pyackett[cloudflare]
14
+ ```
15
+
16
+ ## Quick Start
17
+
18
+ ### As a server
19
+
20
+ ```bash
21
+ # Download definitions from Jackett GitHub and start the server
22
+ pyackett --from-github jackett --port 9117
23
+
24
+ # With a SOCKS5 proxy
25
+ pyackett --from-github jackett --proxy socks5://user:pass@host:1080
26
+
27
+ # With local definitions
28
+ pyackett -d /path/to/definitions
29
+ ```
30
+
31
+ Then point Sonarr/Radarr at `http://localhost:9117/api/v2.0/indexers/{id}/results/torznab/` with the API key shown in the web UI.
32
+
33
+ ### As a Python library
34
+
35
+ ```python
36
+ import asyncio
37
+ from pyackett import Pyackett
38
+
39
+ async def main():
40
+ pk = Pyackett(proxy="socks5://user:pass@host:1080")
41
+ pk.load_definitions_from_github(source="jackett")
42
+
43
+ await pk.configure_indexer("1337x", {})
44
+ await pk.configure_indexer("therarbg", {})
45
+
46
+ results = await pk.search("breaking bad", categories=[5000])
47
+ for r in results:
48
+ print(f"{r.title} | S:{r.seeders} | {r.size}")
49
+
50
+ await pk.close()
51
+
52
+ asyncio.run(main())
53
+ ```
54
+
55
+ ```python
56
+ # Synchronous wrapper for simple scripts
57
+ pk = Pyackett()
58
+ pk.load_definitions_from_github()
59
+ pk.configure_indexer("therarbg", {})
60
+ results = pk.search_sync("ubuntu")
61
+ ```
62
+
63
+ ## Features
64
+
65
+ ### Torznab API
66
+ - Full Torznab XML API compatible with Sonarr, Radarr, Prowlarr, qBittorrent
67
+ - TorrentPotato JSON support
68
+ - Capabilities endpoint per indexer
69
+ - Multi-indexer search (`/indexers/all/results/torznab/`)
70
+
71
+ ### Cardigann YAML Engine
72
+ - Loads all 550+ Jackett YAML indexer definitions
73
+ - Go-style template engine (`{{ if }}`, `{{ range }}`, `{{ eq }}`, etc.)
74
+ - CSS selector extraction (BeautifulSoup) and JSONPath
75
+ - 25+ filter functions (replace, dateparse, fuzzytime, regexp, etc.)
76
+ - 6 login methods (form, cookie, header/API key, GET, POST, captcha)
77
+
78
+ ### HTTP Client
79
+ - **curl_cffi** with Chrome/Firefox TLS fingerprint impersonation
80
+ - Built-in SOCKS5/SOCKS4/HTTP proxy support
81
+ - Request delay per indexer definition
82
+ - Automatic Cloudflare detection and bypass
83
+
84
+ ### Cloudflare Bypass (optional)
85
+ - Automatic detection of Cloudflare 403/503 challenges
86
+ - **Camoufox** (anti-detect Firefox) solves Turnstile challenges headlessly
87
+ - Local HTTP CONNECT proxy bridges SOCKS5 auth for browsers
88
+ - Firefox TLS fingerprint matching for cf_clearance cookie reuse
89
+ - Cookies cached per domain for the session
90
+ - Graceful fallback when Camoufox not installed (logs warning, skips CF sites)
91
+
92
+ ### Web UI
93
+ - Bootstrap 5 dashboard at `http://localhost:9117/`
94
+ - Browse and filter all 550+ available indexers
95
+ - Dynamic configuration forms generated from YAML settings
96
+ - Manual search with results table
97
+ - Per-indexer Torznab URL copy, edit, test, delete
98
+
99
+ ## CLI Options
100
+
101
+ ```
102
+ pyackett [OPTIONS]
103
+
104
+ --host HOST Bind address (default: 0.0.0.0)
105
+ -p, --port PORT Port (default: 9117)
106
+ --config-dir DIR Config/cache directory
107
+ -d, --definitions-dir DIR Local YAML definitions directory
108
+ --from-github {jackett,prowlarr}
109
+ Download definitions from GitHub
110
+ --branch BRANCH GitHub branch (default: master)
111
+ --update-definitions Force re-download definitions
112
+ --proxy URL Proxy (socks5://host:port, http://host:port)
113
+ --api-key KEY API key (auto-generated if not set)
114
+ --log-level {DEBUG,INFO,WARNING,ERROR}
115
+ ```
116
+
117
+ ## Architecture
118
+
119
+ ```
120
+ pyackett/
121
+ core/
122
+ models.py - ReleaseInfo, TorznabQuery, IndexerDefinition
123
+ manager.py - IndexerManager (load, configure, concurrent search)
124
+ http.py - curl_cffi client + Camoufox CF solver
125
+ cache.py - TTL result cache
126
+ categories.py - Torznab category mappings
127
+ definitions_fetcher.py - GitHub tarball downloader
128
+ engine/
129
+ cardigann.py - YAML definition interpreter
130
+ template.py - Go-style template engine
131
+ filters.py - 25+ data transformation filters
132
+ selectors.py - CSS + JSONPath extraction
133
+ api/
134
+ torznab.py - Torznab XML generation + query parsing
135
+ server/
136
+ app.py - FastAPI server + web UI
137
+ pyackett.py - Public library API
138
+ cli.py - CLI entry point
139
+ ```
140
+
141
+ ## Development
142
+
143
+ ```bash
144
+ git clone <repo>
145
+ cd pyackett
146
+ uv sync
147
+ uv run pytest tests/ -v
148
+ ```
149
+
150
+ ## License
151
+
152
+ MIT
@@ -0,0 +1,57 @@
1
+ [project]
2
+ name = "pyackett"
3
+ version = "0.1.0"
4
+ description = "Python clean room implementation of a Torznab-compatible indexer proxy"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ license = "MIT"
8
+ authors = [{name = "David Markey"}]
9
+ keywords = ["torznab", "jackett", "torrent", "indexer", "proxy", "sonarr", "radarr"]
10
+ classifiers = [
11
+ "Development Status :: 4 - Beta",
12
+ "License :: OSI Approved :: MIT License",
13
+ "Programming Language :: Python :: 3.12",
14
+ "Programming Language :: Python :: 3.13",
15
+ "Topic :: Internet :: WWW/HTTP :: Indexing/Search",
16
+ ]
17
+ dependencies = [
18
+ "fastapi>=0.115.0",
19
+ "uvicorn[standard]>=0.32.0",
20
+ "curl_cffi>=0.7.0",
21
+ "beautifulsoup4>=4.12.0",
22
+ "lxml>=5.0.0",
23
+ "pyyaml>=6.0",
24
+ "pydantic>=2.0.0",
25
+ "python-dateutil>=2.9.0",
26
+ "jsonpath-ng>=1.6.0",
27
+ "cachetools>=5.5.0",
28
+ ]
29
+
30
+ [project.optional-dependencies]
31
+ cloudflare = ["camoufox[geoip]>=0.4.11"]
32
+
33
+ [project.urls]
34
+ Homepage = "https://github.com/dmarkey/pyackett"
35
+ Repository = "https://github.com/dmarkey/pyackett"
36
+ Issues = "https://github.com/dmarkey/pyackett/issues"
37
+
38
+ [project.scripts]
39
+ pyackett = "pyackett.cli:main"
40
+
41
+ [build-system]
42
+ requires = ["hatchling"]
43
+ build-backend = "hatchling.build"
44
+
45
+ [tool.hatch.build.targets.wheel]
46
+ packages = ["src/pyackett"]
47
+
48
+ [tool.pytest.ini_options]
49
+ testpaths = ["tests"]
50
+ asyncio_mode = "auto"
51
+
52
+ [dependency-groups]
53
+ dev = [
54
+ "pytest>=8.0.0",
55
+ "pytest-asyncio>=0.24.0",
56
+ "camoufox[geoip]>=0.4.11",
57
+ ]
@@ -0,0 +1,8 @@
1
+ """Pyackett - A Python Torznab-compatible indexer proxy."""
2
+
3
+ from pyackett.core.models import ReleaseInfo, TorznabQuery
4
+ from pyackett.core.manager import IndexerManager
5
+ from pyackett.pyackett import Pyackett
6
+
7
+ __version__ = "0.1.0"
8
+ __all__ = ["Pyackett", "IndexerManager", "ReleaseInfo", "TorznabQuery"]
File without changes