quasarr 2.1.0__tar.gz → 2.1.1__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.
Potentially problematic release.
This version of quasarr might be problematic. Click here for more details.
- {quasarr-2.1.0 → quasarr-2.1.1}/PKG-INFO +38 -22
- {quasarr-2.1.0 → quasarr-2.1.1}/README.md +37 -21
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/api/__init__.py +1 -1
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/api/arr/__init__.py +1 -15
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/api/sponsors_helper/__init__.py +13 -5
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/downloads/__init__.py +23 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/providers/auth.py +16 -1
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/providers/version.py +1 -1
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr.egg-info/PKG-INFO +38 -22
- {quasarr-2.1.0 → quasarr-2.1.1}/LICENSE +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/__init__.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/api/captcha/__init__.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/api/config/__init__.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/api/packages/__init__.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/api/statistics/__init__.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/downloads/linkcrypters/__init__.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/downloads/linkcrypters/al.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/downloads/linkcrypters/filecrypt.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/downloads/linkcrypters/hide.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/downloads/packages/__init__.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/downloads/sources/__init__.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/downloads/sources/al.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/downloads/sources/by.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/downloads/sources/dd.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/downloads/sources/dj.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/downloads/sources/dl.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/downloads/sources/dt.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/downloads/sources/dw.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/downloads/sources/he.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/downloads/sources/mb.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/downloads/sources/nk.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/downloads/sources/nx.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/downloads/sources/sf.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/downloads/sources/sj.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/downloads/sources/sl.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/downloads/sources/wd.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/downloads/sources/wx.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/providers/__init__.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/providers/cloudflare.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/providers/html_images.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/providers/html_templates.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/providers/imdb_metadata.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/providers/jd_cache.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/providers/log.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/providers/myjd_api.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/providers/notifications.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/providers/obfuscated.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/providers/sessions/__init__.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/providers/sessions/al.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/providers/sessions/dd.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/providers/sessions/dl.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/providers/sessions/nx.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/providers/shared_state.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/providers/statistics.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/providers/utils.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/providers/web_server.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/search/__init__.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/search/sources/__init__.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/search/sources/al.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/search/sources/by.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/search/sources/dd.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/search/sources/dj.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/search/sources/dl.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/search/sources/dt.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/search/sources/dw.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/search/sources/fx.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/search/sources/he.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/search/sources/mb.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/search/sources/nk.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/search/sources/nx.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/search/sources/sf.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/search/sources/sj.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/search/sources/sl.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/search/sources/wd.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/search/sources/wx.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/storage/__init__.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/storage/config.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/storage/setup.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr/storage/sqlite_database.py +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr.egg-info/SOURCES.txt +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr.egg-info/dependency_links.txt +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr.egg-info/entry_points.txt +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr.egg-info/not-zip-safe +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr.egg-info/requires.txt +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/quasarr.egg-info/top_level.txt +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/setup.cfg +0 -0
- {quasarr-2.1.0 → quasarr-2.1.1}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: quasarr
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.1
|
|
4
4
|
Summary: Quasarr connects JDownloader with Radarr, Sonarr and LazyLibrarian. It also decrypts links protected by CAPTCHAs.
|
|
5
5
|
Home-page: https://github.com/rix1337/Quasarr
|
|
6
6
|
Author: rix1337
|
|
@@ -25,7 +25,7 @@ Dynamic: license-file
|
|
|
25
25
|
Dynamic: requires-dist
|
|
26
26
|
Dynamic: summary
|
|
27
27
|
|
|
28
|
-
#
|
|
28
|
+
#
|
|
29
29
|
|
|
30
30
|
<img src="https://raw.githubusercontent.com/rix1337/Quasarr/main/Quasarr.png" data-canonical-src="https://raw.githubusercontent.com/rix1337/Quasarr/main/Quasarr.png" width="64" height="64" />
|
|
31
31
|
|
|
@@ -197,18 +197,18 @@ docker run -d \
|
|
|
197
197
|
ghcr.io/rix1337/quasarr:latest
|
|
198
198
|
```
|
|
199
199
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
200
|
+
| Parameter | Description |
|
|
201
|
+
|--------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
202
|
+
| `INTERNAL_ADDRESS` | **Required.** Internal URL so Radarr/Sonarr/LazyLibrarian can reach Quasarr. **Must include port.** |
|
|
203
|
+
| `EXTERNAL_ADDRESS` | Optional. External URL (e.g. reverse proxy). Always protect external access with authentication. |
|
|
204
|
+
| `DISCORD` | Optional. Discord webhook URL for notifications. |
|
|
205
|
+
| `HOSTNAMES` | Optional. URL to a hostname list to skip manual setup. Must be a publicly accessible `HTTP`/`HTTPS` link, point to a raw `.ini` or plain text file (not HTML or JSON), and contain at least one hostname per line in the format `ab = xyz`. |
|
|
206
|
+
| `USER` | Username to protect the web UI. |
|
|
207
|
+
| `PASS` | Password to protect the web UI. |
|
|
208
|
+
| `AUTH` | Authentication mode. Supported values: `form` or `basic`. |
|
|
209
|
+
| `SILENT` | Optional. If `True`, silences all Discord notifications except SponsorHelper error messages. |
|
|
210
|
+
| `DEBUG` | Optional. If `True`, enables debug logging. |
|
|
211
|
+
| `TZ` | Optional. Timezone. Incorrect values may cause HTTPS/SSL issues. |
|
|
212
212
|
|
|
213
213
|
# Manual setup
|
|
214
214
|
|
|
@@ -280,9 +280,21 @@ Image access is limited to [active monthly GitHub sponsors](https://github.com/u
|
|
|
280
280
|
|
|
281
281
|
---
|
|
282
282
|
|
|
283
|
+
## 🔐 Quasarr API Key Setup
|
|
284
|
+
|
|
285
|
+
1. Open your Quasarr web UI in a browser
|
|
286
|
+
2. On the main page, expand **"Show API Settings"**
|
|
287
|
+
3. Copy the **API Key** value
|
|
288
|
+
4. Use this value for the `QUASARR_API_KEY` environment variable
|
|
289
|
+
|
|
290
|
+
> **Note:** The API key is required for SponsorsHelper to communicate securely with Quasarr. Without it, all requests
|
|
291
|
+
> will be rejected with a 401/403 error.
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
283
295
|
## 🐋 Docker Login
|
|
284
296
|
|
|
285
|
-
⚠️ **
|
|
297
|
+
⚠️ **If not logged in, the image will not download.**
|
|
286
298
|
|
|
287
299
|
```bash
|
|
288
300
|
echo "GITHUB_TOKEN" | docker login ghcr.io -u USERNAME --password-stdin
|
|
@@ -299,6 +311,7 @@ echo "GITHUB_TOKEN" | docker login ghcr.io -u USERNAME --password-stdin
|
|
|
299
311
|
docker run -d \
|
|
300
312
|
--name='SponsorsHelper' \
|
|
301
313
|
-e 'QUASARR_URL'='http://192.168.0.1:8080' \
|
|
314
|
+
-e 'QUASARR_API_KEY'='your_quasarr_api_key_here' \
|
|
302
315
|
-e 'DEATHBYCAPTCHA_TOKEN'='2FMum5zuDBxMmbXDIsADnllEFl73bomydIpzo7...' \
|
|
303
316
|
-e 'GITHUB_TOKEN'='ghp_123.....456789' \
|
|
304
317
|
-e 'FLARESOLVERR_URL'='http://10.10.0.1:8191/v1' \
|
|
@@ -310,10 +323,13 @@ docker run -d \
|
|
|
310
323
|
ghcr.io/rix1337-sponsors/docker/helper:latest
|
|
311
324
|
```
|
|
312
325
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
326
|
+
| Parameter | Description |
|
|
327
|
+
|---------------------------------|---------------------------------------------------------------------------------------|
|
|
328
|
+
| `QUASARR_URL` | Local URL of Quasarr (e.g., `http://192.168.0.1:8080`) |
|
|
329
|
+
| `QUASARR_API_KEY` | Your Quasarr API key (found in Quasarr web UI under "API Settings") |
|
|
330
|
+
| `DEATHBYCAPTCHA_TOKEN` | [DeathByCaptcha](https://deathbycaptcha.com/register?refid=6184288242b) account token |
|
|
331
|
+
| `GITHUB_TOKEN` | Classic GitHub PAT with the scopes listed above |
|
|
332
|
+
| `FLARESOLVERR_URL` | Local URL of [FlareSolverr](https://github.com/FlareSolverr/FlareSolverr) |
|
|
333
|
+
| `NX_USER` / `NX_PASS` | Optional. NX account credentials |
|
|
334
|
+
| `JUNKIES_USER` / `JUNKIES_PASS` | Optional. Junkies account credentials |
|
|
335
|
+
| `JUNKIES_HOSTER` | Optional. Preferred hoster for Junkies links |
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
#
|
|
2
2
|
|
|
3
3
|
<img src="https://raw.githubusercontent.com/rix1337/Quasarr/main/Quasarr.png" data-canonical-src="https://raw.githubusercontent.com/rix1337/Quasarr/main/Quasarr.png" width="64" height="64" />
|
|
4
4
|
|
|
@@ -170,18 +170,18 @@ docker run -d \
|
|
|
170
170
|
ghcr.io/rix1337/quasarr:latest
|
|
171
171
|
```
|
|
172
172
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
173
|
+
| Parameter | Description |
|
|
174
|
+
|--------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
175
|
+
| `INTERNAL_ADDRESS` | **Required.** Internal URL so Radarr/Sonarr/LazyLibrarian can reach Quasarr. **Must include port.** |
|
|
176
|
+
| `EXTERNAL_ADDRESS` | Optional. External URL (e.g. reverse proxy). Always protect external access with authentication. |
|
|
177
|
+
| `DISCORD` | Optional. Discord webhook URL for notifications. |
|
|
178
|
+
| `HOSTNAMES` | Optional. URL to a hostname list to skip manual setup. Must be a publicly accessible `HTTP`/`HTTPS` link, point to a raw `.ini` or plain text file (not HTML or JSON), and contain at least one hostname per line in the format `ab = xyz`. |
|
|
179
|
+
| `USER` | Username to protect the web UI. |
|
|
180
|
+
| `PASS` | Password to protect the web UI. |
|
|
181
|
+
| `AUTH` | Authentication mode. Supported values: `form` or `basic`. |
|
|
182
|
+
| `SILENT` | Optional. If `True`, silences all Discord notifications except SponsorHelper error messages. |
|
|
183
|
+
| `DEBUG` | Optional. If `True`, enables debug logging. |
|
|
184
|
+
| `TZ` | Optional. Timezone. Incorrect values may cause HTTPS/SSL issues. |
|
|
185
185
|
|
|
186
186
|
# Manual setup
|
|
187
187
|
|
|
@@ -253,9 +253,21 @@ Image access is limited to [active monthly GitHub sponsors](https://github.com/u
|
|
|
253
253
|
|
|
254
254
|
---
|
|
255
255
|
|
|
256
|
+
## 🔐 Quasarr API Key Setup
|
|
257
|
+
|
|
258
|
+
1. Open your Quasarr web UI in a browser
|
|
259
|
+
2. On the main page, expand **"Show API Settings"**
|
|
260
|
+
3. Copy the **API Key** value
|
|
261
|
+
4. Use this value for the `QUASARR_API_KEY` environment variable
|
|
262
|
+
|
|
263
|
+
> **Note:** The API key is required for SponsorsHelper to communicate securely with Quasarr. Without it, all requests
|
|
264
|
+
> will be rejected with a 401/403 error.
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
256
268
|
## 🐋 Docker Login
|
|
257
269
|
|
|
258
|
-
⚠️ **
|
|
270
|
+
⚠️ **If not logged in, the image will not download.**
|
|
259
271
|
|
|
260
272
|
```bash
|
|
261
273
|
echo "GITHUB_TOKEN" | docker login ghcr.io -u USERNAME --password-stdin
|
|
@@ -272,6 +284,7 @@ echo "GITHUB_TOKEN" | docker login ghcr.io -u USERNAME --password-stdin
|
|
|
272
284
|
docker run -d \
|
|
273
285
|
--name='SponsorsHelper' \
|
|
274
286
|
-e 'QUASARR_URL'='http://192.168.0.1:8080' \
|
|
287
|
+
-e 'QUASARR_API_KEY'='your_quasarr_api_key_here' \
|
|
275
288
|
-e 'DEATHBYCAPTCHA_TOKEN'='2FMum5zuDBxMmbXDIsADnllEFl73bomydIpzo7...' \
|
|
276
289
|
-e 'GITHUB_TOKEN'='ghp_123.....456789' \
|
|
277
290
|
-e 'FLARESOLVERR_URL'='http://10.10.0.1:8191/v1' \
|
|
@@ -283,10 +296,13 @@ docker run -d \
|
|
|
283
296
|
ghcr.io/rix1337-sponsors/docker/helper:latest
|
|
284
297
|
```
|
|
285
298
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
299
|
+
| Parameter | Description |
|
|
300
|
+
|---------------------------------|---------------------------------------------------------------------------------------|
|
|
301
|
+
| `QUASARR_URL` | Local URL of Quasarr (e.g., `http://192.168.0.1:8080`) |
|
|
302
|
+
| `QUASARR_API_KEY` | Your Quasarr API key (found in Quasarr web UI under "API Settings") |
|
|
303
|
+
| `DEATHBYCAPTCHA_TOKEN` | [DeathByCaptcha](https://deathbycaptcha.com/register?refid=6184288242b) account token |
|
|
304
|
+
| `GITHUB_TOKEN` | Classic GitHub PAT with the scopes listed above |
|
|
305
|
+
| `FLARESOLVERR_URL` | Local URL of [FlareSolverr](https://github.com/FlareSolverr/FlareSolverr) |
|
|
306
|
+
| `NX_USER` / `NX_PASS` | Optional. NX account credentials |
|
|
307
|
+
| `JUNKIES_USER` / `JUNKIES_PASS` | Optional. Junkies account credentials |
|
|
308
|
+
| `JUNKIES_HOSTER` | Optional. Preferred hoster for Junkies links |
|
|
@@ -25,7 +25,7 @@ def get_api(shared_state_dict, shared_state_lock):
|
|
|
25
25
|
|
|
26
26
|
# Auth: routes must come first, then hook
|
|
27
27
|
add_auth_routes(app)
|
|
28
|
-
add_auth_hook(app, whitelist_prefixes=['/api', '/api/' '/sponsors_helper/', '/download/'])
|
|
28
|
+
add_auth_hook(app, whitelist_prefixes=['/api', '/api/', '/sponsors_helper/', '/download/'])
|
|
29
29
|
|
|
30
30
|
setup_arr_routes(app)
|
|
31
31
|
setup_captcha_routes(app)
|
|
@@ -6,7 +6,6 @@ import traceback
|
|
|
6
6
|
import xml.sax.saxutils as sax_utils
|
|
7
7
|
from base64 import urlsafe_b64decode
|
|
8
8
|
from datetime import datetime
|
|
9
|
-
from functools import wraps
|
|
10
9
|
from urllib.parse import urlparse, parse_qs
|
|
11
10
|
from xml.etree import ElementTree
|
|
12
11
|
|
|
@@ -15,23 +14,10 @@ from bottle import abort, request
|
|
|
15
14
|
from quasarr.downloads import download
|
|
16
15
|
from quasarr.downloads.packages import get_packages, delete_package
|
|
17
16
|
from quasarr.providers import shared_state
|
|
17
|
+
from quasarr.providers.auth import require_api_key
|
|
18
18
|
from quasarr.providers.log import info, debug
|
|
19
19
|
from quasarr.providers.version import get_version
|
|
20
20
|
from quasarr.search import get_search_results
|
|
21
|
-
from quasarr.storage.config import Config
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def require_api_key(func):
|
|
25
|
-
@wraps(func)
|
|
26
|
-
def decorated(*args, **kwargs):
|
|
27
|
-
api_key = Config('API').get('key')
|
|
28
|
-
if not request.query.apikey:
|
|
29
|
-
return abort(401, "Missing API key")
|
|
30
|
-
if request.query.apikey != api_key:
|
|
31
|
-
return abort(403, "Invalid API key")
|
|
32
|
-
return func(*args, **kwargs)
|
|
33
|
-
|
|
34
|
-
return decorated
|
|
35
21
|
|
|
36
22
|
|
|
37
23
|
def parse_payload(payload_str):
|
|
@@ -8,13 +8,21 @@ from bottle import request, abort
|
|
|
8
8
|
|
|
9
9
|
from quasarr.downloads import fail
|
|
10
10
|
from quasarr.providers import shared_state
|
|
11
|
+
from quasarr.providers.auth import require_api_key
|
|
11
12
|
from quasarr.providers.log import info
|
|
12
13
|
from quasarr.providers.notifications import send_discord_message
|
|
13
14
|
from quasarr.providers.statistics import StatsHelper
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
def setup_sponsors_helper_routes(app):
|
|
18
|
+
@app.get("/sponsors_helper/api/ping/")
|
|
19
|
+
@require_api_key
|
|
20
|
+
def ping_api():
|
|
21
|
+
"""Health check endpoint for SponsorsHelper to verify connectivity."""
|
|
22
|
+
return "pong"
|
|
23
|
+
|
|
17
24
|
@app.get("/sponsors_helper/api/to_decrypt/")
|
|
25
|
+
@require_api_key
|
|
18
26
|
def to_decrypt_api():
|
|
19
27
|
try:
|
|
20
28
|
if not shared_state.values["helper_active"]:
|
|
@@ -60,6 +68,7 @@ def setup_sponsors_helper_routes(app):
|
|
|
60
68
|
return abort(500, str(e))
|
|
61
69
|
|
|
62
70
|
@app.post("/sponsors_helper/api/to_download/")
|
|
71
|
+
@require_api_key
|
|
63
72
|
def to_download_api():
|
|
64
73
|
try:
|
|
65
74
|
data = request.json
|
|
@@ -86,9 +95,10 @@ def setup_sponsors_helper_routes(app):
|
|
|
86
95
|
info(f"Error decrypting: {e}")
|
|
87
96
|
|
|
88
97
|
StatsHelper(shared_state).increment_failed_decryptions_automatic()
|
|
89
|
-
return abort(500, "Failed")
|
|
98
|
+
return abort(500, "Failed")
|
|
90
99
|
|
|
91
100
|
@app.post("/sponsors_helper/api/to_replace/")
|
|
101
|
+
@require_api_key
|
|
92
102
|
def to_replace_api():
|
|
93
103
|
try:
|
|
94
104
|
data = request.json
|
|
@@ -130,6 +140,7 @@ def setup_sponsors_helper_routes(app):
|
|
|
130
140
|
return {"error": str(e)}, 500
|
|
131
141
|
|
|
132
142
|
@app.delete("/sponsors_helper/api/to_failed/")
|
|
143
|
+
@require_api_key
|
|
133
144
|
def move_to_failed_api():
|
|
134
145
|
try:
|
|
135
146
|
StatsHelper(shared_state).increment_failed_decryptions_automatic()
|
|
@@ -153,6 +164,7 @@ def setup_sponsors_helper_routes(app):
|
|
|
153
164
|
return abort(500, "Failed")
|
|
154
165
|
|
|
155
166
|
@app.put("/sponsors_helper/api/set_sponsor_status/")
|
|
167
|
+
@require_api_key
|
|
156
168
|
def activate_sponsor_status():
|
|
157
169
|
try:
|
|
158
170
|
data = request.body.read().decode("utf-8")
|
|
@@ -164,7 +176,3 @@ def setup_sponsors_helper_routes(app):
|
|
|
164
176
|
except:
|
|
165
177
|
pass
|
|
166
178
|
return abort(500, "Failed")
|
|
167
|
-
|
|
168
|
-
@app.get("/sponsors_helper/api/ping/")
|
|
169
|
-
def get_sponsor_status():
|
|
170
|
-
return "pong"
|
|
@@ -7,6 +7,7 @@ import json
|
|
|
7
7
|
import re
|
|
8
8
|
|
|
9
9
|
from quasarr.downloads.linkcrypters.hide import decrypt_links_if_hide
|
|
10
|
+
from quasarr.downloads.packages import get_packages
|
|
10
11
|
from quasarr.downloads.sources.al import get_al_download_links
|
|
11
12
|
from quasarr.downloads.sources.by import get_by_download_links
|
|
12
13
|
from quasarr.downloads.sources.dd import get_dd_download_links
|
|
@@ -295,6 +296,23 @@ def process_links(shared_state, source_result, title, password, package_id, imdb
|
|
|
295
296
|
# MAIN ENTRY POINT
|
|
296
297
|
# =============================================================================
|
|
297
298
|
|
|
299
|
+
def package_id_exists(shared_state, package_id):
|
|
300
|
+
# DB checks
|
|
301
|
+
if shared_state.get_db("protected").retrieve(package_id):
|
|
302
|
+
return True
|
|
303
|
+
if shared_state.get_db("failed").retrieve(package_id):
|
|
304
|
+
return True
|
|
305
|
+
|
|
306
|
+
data = get_packages(shared_state) or {}
|
|
307
|
+
|
|
308
|
+
for section in ("queue", "history"):
|
|
309
|
+
for pkg in data.get(section, []) or []:
|
|
310
|
+
if pkg.get("nzo_id") == package_id:
|
|
311
|
+
return True
|
|
312
|
+
|
|
313
|
+
return False
|
|
314
|
+
|
|
315
|
+
|
|
298
316
|
def download(shared_state, request_from, title, url, mirror, size_mb, password, imdb_id=None, source_key=None):
|
|
299
317
|
"""
|
|
300
318
|
Main download entry point.
|
|
@@ -348,6 +366,11 @@ def download(shared_state, request_from, title, url, mirror, size_mb, password,
|
|
|
348
366
|
# Generate DETERMINISTIC package_id
|
|
349
367
|
package_id = generate_deterministic_package_id(title, final_source_key, client_type)
|
|
350
368
|
|
|
369
|
+
# Skip Download if package_id already exists
|
|
370
|
+
if package_id_exists(shared_state, package_id):
|
|
371
|
+
info(f"Package {package_id} already exists. Skipping download!")
|
|
372
|
+
return {"success": True, "package_id": package_id, "title": title}
|
|
373
|
+
|
|
351
374
|
if source_result is None:
|
|
352
375
|
info(f'Could not find matching source for "{title}" - "{url}"')
|
|
353
376
|
StatsHelper(shared_state).increment_failed_downloads()
|
|
@@ -7,11 +7,13 @@ import hashlib
|
|
|
7
7
|
import hmac
|
|
8
8
|
import os
|
|
9
9
|
import time
|
|
10
|
+
from functools import wraps
|
|
10
11
|
|
|
11
|
-
from bottle import request, response, redirect
|
|
12
|
+
from bottle import request, response, redirect, abort
|
|
12
13
|
|
|
13
14
|
import quasarr.providers.html_images as images
|
|
14
15
|
from quasarr.providers.version import get_version
|
|
16
|
+
from quasarr.storage.config import Config
|
|
15
17
|
|
|
16
18
|
# Auth configuration from environment
|
|
17
19
|
AUTH_USER = os.environ.get('USER', '')
|
|
@@ -248,3 +250,16 @@ def add_auth_hook(app, whitelist_prefixes=None):
|
|
|
248
250
|
else:
|
|
249
251
|
if not check_basic_auth():
|
|
250
252
|
return require_basic_auth()
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def require_api_key(func):
|
|
256
|
+
@wraps(func)
|
|
257
|
+
def decorated(*args, **kwargs):
|
|
258
|
+
api_key = Config('API').get('key')
|
|
259
|
+
if not request.query.apikey:
|
|
260
|
+
return abort(401, "Missing API key")
|
|
261
|
+
if request.query.apikey != api_key:
|
|
262
|
+
return abort(403, "Invalid API key")
|
|
263
|
+
return func(*args, **kwargs)
|
|
264
|
+
|
|
265
|
+
return decorated
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: quasarr
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.1
|
|
4
4
|
Summary: Quasarr connects JDownloader with Radarr, Sonarr and LazyLibrarian. It also decrypts links protected by CAPTCHAs.
|
|
5
5
|
Home-page: https://github.com/rix1337/Quasarr
|
|
6
6
|
Author: rix1337
|
|
@@ -25,7 +25,7 @@ Dynamic: license-file
|
|
|
25
25
|
Dynamic: requires-dist
|
|
26
26
|
Dynamic: summary
|
|
27
27
|
|
|
28
|
-
#
|
|
28
|
+
#
|
|
29
29
|
|
|
30
30
|
<img src="https://raw.githubusercontent.com/rix1337/Quasarr/main/Quasarr.png" data-canonical-src="https://raw.githubusercontent.com/rix1337/Quasarr/main/Quasarr.png" width="64" height="64" />
|
|
31
31
|
|
|
@@ -197,18 +197,18 @@ docker run -d \
|
|
|
197
197
|
ghcr.io/rix1337/quasarr:latest
|
|
198
198
|
```
|
|
199
199
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
200
|
+
| Parameter | Description |
|
|
201
|
+
|--------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
202
|
+
| `INTERNAL_ADDRESS` | **Required.** Internal URL so Radarr/Sonarr/LazyLibrarian can reach Quasarr. **Must include port.** |
|
|
203
|
+
| `EXTERNAL_ADDRESS` | Optional. External URL (e.g. reverse proxy). Always protect external access with authentication. |
|
|
204
|
+
| `DISCORD` | Optional. Discord webhook URL for notifications. |
|
|
205
|
+
| `HOSTNAMES` | Optional. URL to a hostname list to skip manual setup. Must be a publicly accessible `HTTP`/`HTTPS` link, point to a raw `.ini` or plain text file (not HTML or JSON), and contain at least one hostname per line in the format `ab = xyz`. |
|
|
206
|
+
| `USER` | Username to protect the web UI. |
|
|
207
|
+
| `PASS` | Password to protect the web UI. |
|
|
208
|
+
| `AUTH` | Authentication mode. Supported values: `form` or `basic`. |
|
|
209
|
+
| `SILENT` | Optional. If `True`, silences all Discord notifications except SponsorHelper error messages. |
|
|
210
|
+
| `DEBUG` | Optional. If `True`, enables debug logging. |
|
|
211
|
+
| `TZ` | Optional. Timezone. Incorrect values may cause HTTPS/SSL issues. |
|
|
212
212
|
|
|
213
213
|
# Manual setup
|
|
214
214
|
|
|
@@ -280,9 +280,21 @@ Image access is limited to [active monthly GitHub sponsors](https://github.com/u
|
|
|
280
280
|
|
|
281
281
|
---
|
|
282
282
|
|
|
283
|
+
## 🔐 Quasarr API Key Setup
|
|
284
|
+
|
|
285
|
+
1. Open your Quasarr web UI in a browser
|
|
286
|
+
2. On the main page, expand **"Show API Settings"**
|
|
287
|
+
3. Copy the **API Key** value
|
|
288
|
+
4. Use this value for the `QUASARR_API_KEY` environment variable
|
|
289
|
+
|
|
290
|
+
> **Note:** The API key is required for SponsorsHelper to communicate securely with Quasarr. Without it, all requests
|
|
291
|
+
> will be rejected with a 401/403 error.
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
283
295
|
## 🐋 Docker Login
|
|
284
296
|
|
|
285
|
-
⚠️ **
|
|
297
|
+
⚠️ **If not logged in, the image will not download.**
|
|
286
298
|
|
|
287
299
|
```bash
|
|
288
300
|
echo "GITHUB_TOKEN" | docker login ghcr.io -u USERNAME --password-stdin
|
|
@@ -299,6 +311,7 @@ echo "GITHUB_TOKEN" | docker login ghcr.io -u USERNAME --password-stdin
|
|
|
299
311
|
docker run -d \
|
|
300
312
|
--name='SponsorsHelper' \
|
|
301
313
|
-e 'QUASARR_URL'='http://192.168.0.1:8080' \
|
|
314
|
+
-e 'QUASARR_API_KEY'='your_quasarr_api_key_here' \
|
|
302
315
|
-e 'DEATHBYCAPTCHA_TOKEN'='2FMum5zuDBxMmbXDIsADnllEFl73bomydIpzo7...' \
|
|
303
316
|
-e 'GITHUB_TOKEN'='ghp_123.....456789' \
|
|
304
317
|
-e 'FLARESOLVERR_URL'='http://10.10.0.1:8191/v1' \
|
|
@@ -310,10 +323,13 @@ docker run -d \
|
|
|
310
323
|
ghcr.io/rix1337-sponsors/docker/helper:latest
|
|
311
324
|
```
|
|
312
325
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
326
|
+
| Parameter | Description |
|
|
327
|
+
|---------------------------------|---------------------------------------------------------------------------------------|
|
|
328
|
+
| `QUASARR_URL` | Local URL of Quasarr (e.g., `http://192.168.0.1:8080`) |
|
|
329
|
+
| `QUASARR_API_KEY` | Your Quasarr API key (found in Quasarr web UI under "API Settings") |
|
|
330
|
+
| `DEATHBYCAPTCHA_TOKEN` | [DeathByCaptcha](https://deathbycaptcha.com/register?refid=6184288242b) account token |
|
|
331
|
+
| `GITHUB_TOKEN` | Classic GitHub PAT with the scopes listed above |
|
|
332
|
+
| `FLARESOLVERR_URL` | Local URL of [FlareSolverr](https://github.com/FlareSolverr/FlareSolverr) |
|
|
333
|
+
| `NX_USER` / `NX_PASS` | Optional. NX account credentials |
|
|
334
|
+
| `JUNKIES_USER` / `JUNKIES_PASS` | Optional. Junkies account credentials |
|
|
335
|
+
| `JUNKIES_HOSTER` | Optional. Preferred hoster for Junkies links |
|
|
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
|
|
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
|
|
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
|