quasarr 1.17.3__tar.gz → 1.19.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.
Potentially problematic release.
This version of quasarr might be problematic. Click here for more details.
- {quasarr-1.17.3 → quasarr-1.19.0}/PKG-INFO +1 -1
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/api/captcha/__init__.py +56 -1
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/downloads/__init__.py +6 -0
- quasarr-1.19.0/quasarr/downloads/sources/he.py +112 -0
- quasarr-1.19.0/quasarr/downloads/sources/nk.py +51 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/providers/html_images.py +2 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/providers/obfuscated.py +18 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/providers/version.py +1 -1
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/search/__init__.py +8 -0
- quasarr-1.19.0/quasarr/search/sources/he.py +196 -0
- quasarr-1.19.0/quasarr/search/sources/nk.py +188 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/storage/config.py +2 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr.egg-info/PKG-INFO +1 -1
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr.egg-info/SOURCES.txt +4 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/LICENSE +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/README.md +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/__init__.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/api/__init__.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/api/arr/__init__.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/api/config/__init__.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/api/sponsors_helper/__init__.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/api/statistics/__init__.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/downloads/linkcrypters/__init__.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/downloads/linkcrypters/al.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/downloads/linkcrypters/filecrypt.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/downloads/linkcrypters/hide.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/downloads/packages/__init__.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/downloads/sources/__init__.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/downloads/sources/al.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/downloads/sources/by.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/downloads/sources/dd.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/downloads/sources/dt.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/downloads/sources/dw.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/downloads/sources/mb.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/downloads/sources/nx.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/downloads/sources/sf.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/downloads/sources/sl.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/downloads/sources/wd.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/providers/__init__.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/providers/cloudflare.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/providers/html_templates.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/providers/imdb_metadata.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/providers/log.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/providers/myjd_api.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/providers/notifications.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/providers/sessions/__init__.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/providers/sessions/al.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/providers/sessions/dd.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/providers/sessions/nx.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/providers/shared_state.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/providers/statistics.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/providers/web_server.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/search/sources/__init__.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/search/sources/al.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/search/sources/by.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/search/sources/dd.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/search/sources/dt.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/search/sources/dw.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/search/sources/fx.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/search/sources/mb.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/search/sources/nx.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/search/sources/sf.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/search/sources/sl.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/search/sources/wd.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/storage/__init__.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/storage/setup.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr/storage/sqlite_database.py +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr.egg-info/dependency_links.txt +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr.egg-info/entry_points.txt +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr.egg-info/not-zip-safe +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr.egg-info/requires.txt +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/quasarr.egg-info/top_level.txt +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/setup.cfg +0 -0
- {quasarr-1.17.3 → quasarr-1.19.0}/setup.py +0 -0
|
@@ -16,7 +16,7 @@ from quasarr.downloads.packages import delete_package
|
|
|
16
16
|
from quasarr.providers import shared_state
|
|
17
17
|
from quasarr.providers.html_templates import render_button, render_centered_html
|
|
18
18
|
from quasarr.providers.log import info, debug
|
|
19
|
-
from quasarr.providers.obfuscated import captcha_js, captcha_values
|
|
19
|
+
from quasarr.providers.obfuscated import captcha_js, captcha_values, filecrypt_quasarr_helper_user_js
|
|
20
20
|
from quasarr.providers.statistics import StatsHelper
|
|
21
21
|
|
|
22
22
|
|
|
@@ -101,6 +101,12 @@ def setup_captcha_routes(app):
|
|
|
101
101
|
except Exception as e:
|
|
102
102
|
return {"error": f"Failed to decode payload: {str(e)}"}
|
|
103
103
|
|
|
104
|
+
@app.get('/captcha/quasarr.user.js')
|
|
105
|
+
def serve_quasarr_user_js():
|
|
106
|
+
content = filecrypt_quasarr_helper_user_js()
|
|
107
|
+
response.content_type = 'application/javascript'
|
|
108
|
+
return content
|
|
109
|
+
|
|
104
110
|
def render_bypass_section(url, package_id, title, password):
|
|
105
111
|
"""Render the bypass UI section for both cutcaptcha and circle captcha pages"""
|
|
106
112
|
|
|
@@ -121,7 +127,30 @@ def setup_captcha_routes(app):
|
|
|
121
127
|
<div style="margin-top: 40px; padding-top: 20px; border-top: 2px solid #ccc;">
|
|
122
128
|
<details id="bypassDetails">
|
|
123
129
|
<summary id="bypassSummary">Show CAPTCHA Bypass</summary><br>
|
|
130
|
+
|
|
131
|
+
<!-- One-time setup section - visually separated -->
|
|
132
|
+
<div id="setup-instructions" style="background: #2a2a2a; border: 2px solid #444; border-radius: 8px; padding: 16px; margin-bottom: 24px;">
|
|
133
|
+
<h3 style="margin-top: 0; color: #58a6ff;">First Time Setup:</h3>
|
|
134
|
+
<p style="margin-bottom: 8px;">
|
|
135
|
+
<a href="https://www.tampermonkey.net/" target="_blank" rel="noopener noreferrer">1. Install Tampermonkey</a>
|
|
136
|
+
</p>
|
|
137
|
+
<p style="margin-top: 0; margin-bottom: 12px;">
|
|
138
|
+
<a href="/captcha/quasarr.user.js" target="_blank">2. Install this userscript</a>
|
|
139
|
+
</p>
|
|
140
|
+
<p style="margin-top: 0;">
|
|
141
|
+
<button id="hide-setup-btn" type="button" style="background: #444; color: #fff; border: 1px solid #666; padding: 6px 12px; border-radius: 4px; cursor: pointer;">
|
|
142
|
+
✅ Don't show this again
|
|
143
|
+
</button>
|
|
144
|
+
</p>
|
|
145
|
+
</div>
|
|
146
|
+
|
|
147
|
+
<!-- Hidden "show instructions" link -->
|
|
148
|
+
<div id="show-instructions-link" style="display: none; margin-bottom: 16px;">
|
|
149
|
+
<a href="#" id="show-setup-btn" style="color: #58a6ff; text-decoration: underline;">ℹ️ Show instructions again</a>
|
|
150
|
+
</div>
|
|
151
|
+
|
|
124
152
|
<strong><a href="{url_with_quick_transfer_params}" target="_blank">🔗 Obtain the download links here!</a></strong><br><br>
|
|
153
|
+
|
|
125
154
|
<form id="bypass-form" action="/captcha/bypass-submit" method="post" enctype="multipart/form-data">
|
|
126
155
|
<input type="hidden" name="package_id" value="{package_id}" />
|
|
127
156
|
<input type="hidden" name="title" value="{title}" />
|
|
@@ -144,6 +173,7 @@ def setup_captcha_routes(app):
|
|
|
144
173
|
</details>
|
|
145
174
|
</div>
|
|
146
175
|
<script>
|
|
176
|
+
// Handle CAPTCHA Bypass toggle
|
|
147
177
|
const bypassDetails = document.getElementById('bypassDetails');
|
|
148
178
|
const bypassSummary = document.getElementById('bypassSummary');
|
|
149
179
|
|
|
@@ -156,6 +186,31 @@ def setup_captcha_routes(app):
|
|
|
156
186
|
}}
|
|
157
187
|
}});
|
|
158
188
|
}}
|
|
189
|
+
|
|
190
|
+
// Handle setup instructions hide/show
|
|
191
|
+
const hideSetup = localStorage.getItem('hideSetupInstructions');
|
|
192
|
+
const setupBox = document.getElementById('setup-instructions');
|
|
193
|
+
const showLink = document.getElementById('show-instructions-link');
|
|
194
|
+
|
|
195
|
+
if (hideSetup === 'true') {{
|
|
196
|
+
setupBox.style.display = 'none';
|
|
197
|
+
showLink.style.display = 'block';
|
|
198
|
+
}}
|
|
199
|
+
|
|
200
|
+
// Hide setup instructions
|
|
201
|
+
document.getElementById('hide-setup-btn').addEventListener('click', function() {{
|
|
202
|
+
localStorage.setItem('hideSetupInstructions', 'true');
|
|
203
|
+
setupBox.style.display = 'none';
|
|
204
|
+
showLink.style.display = 'block';
|
|
205
|
+
}});
|
|
206
|
+
|
|
207
|
+
// Show setup instructions again
|
|
208
|
+
document.getElementById('show-setup-btn').addEventListener('click', function(e) {{
|
|
209
|
+
e.preventDefault();
|
|
210
|
+
localStorage.setItem('hideSetupInstructions', 'false');
|
|
211
|
+
setupBox.style.display = 'block';
|
|
212
|
+
showLink.style.display = 'none';
|
|
213
|
+
}});
|
|
159
214
|
</script>
|
|
160
215
|
'''
|
|
161
216
|
|
|
@@ -13,7 +13,9 @@ from quasarr.downloads.sources.by import get_by_download_links
|
|
|
13
13
|
from quasarr.downloads.sources.dd import get_dd_download_links
|
|
14
14
|
from quasarr.downloads.sources.dt import get_dt_download_links
|
|
15
15
|
from quasarr.downloads.sources.dw import get_dw_download_links
|
|
16
|
+
from quasarr.downloads.sources.he import get_he_download_links
|
|
16
17
|
from quasarr.downloads.sources.mb import get_mb_download_links
|
|
18
|
+
from quasarr.downloads.sources.nk import get_nk_download_links
|
|
17
19
|
from quasarr.downloads.sources.nx import get_nx_download_links
|
|
18
20
|
from quasarr.downloads.sources.sf import get_sf_download_links, resolve_sf_redirect
|
|
19
21
|
from quasarr.downloads.sources.sl import get_sl_download_links
|
|
@@ -201,7 +203,9 @@ def download(shared_state, request_from, title, url, mirror, size_mb, password,
|
|
|
201
203
|
'DD': config.get("dd"),
|
|
202
204
|
'DT': config.get("dt"),
|
|
203
205
|
'DW': config.get("dw"),
|
|
206
|
+
'HE': config.get("he"),
|
|
204
207
|
'MB': config.get("mb"),
|
|
208
|
+
'NK': config.get("nk"),
|
|
205
209
|
'NX': config.get("nx"),
|
|
206
210
|
'SF': config.get("sf"),
|
|
207
211
|
'SL': config.get("sl"),
|
|
@@ -214,7 +218,9 @@ def download(shared_state, request_from, title, url, mirror, size_mb, password,
|
|
|
214
218
|
(flags['DD'], lambda *a: handle_unprotected(*a, func=get_dd_download_links, label='DD')),
|
|
215
219
|
(flags['DT'], lambda *a: handle_unprotected(*a, func=get_dt_download_links, label='DT')),
|
|
216
220
|
(flags['DW'], lambda *a: handle_protected(*a, func=get_dw_download_links, label='DW')),
|
|
221
|
+
(flags['HE'], lambda *a: handle_unprotected(*a, func=get_he_download_links, label='HE')),
|
|
217
222
|
(flags['MB'], lambda *a: handle_protected(*a, func=get_mb_download_links, label='MB')),
|
|
223
|
+
(flags['NK'], lambda *a: handle_protected(*a, func=get_nk_download_links, label='NK')),
|
|
218
224
|
(flags['NX'], lambda *a: handle_unprotected(*a, func=get_nx_download_links, label='NX')),
|
|
219
225
|
(flags['SF'], handle_sf),
|
|
220
226
|
(flags['SL'], handle_sl),
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Quasarr
|
|
3
|
+
# Project by https://github.com/rix1337
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
from urllib.parse import urlparse, urljoin
|
|
7
|
+
|
|
8
|
+
import requests
|
|
9
|
+
from bs4 import BeautifulSoup
|
|
10
|
+
|
|
11
|
+
from quasarr.providers.log import info, debug
|
|
12
|
+
|
|
13
|
+
hostname = "he"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_he_download_links(shared_state, url, mirror, title):
|
|
17
|
+
headers = {
|
|
18
|
+
'User-Agent': shared_state.values["user_agent"],
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
session = requests.Session()
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
resp = session.get(url, headers=headers, timeout=30)
|
|
25
|
+
soup = BeautifulSoup(resp.text, 'html.parser')
|
|
26
|
+
except Exception as e:
|
|
27
|
+
info(f"{hostname}: could not fetch release for {title}: {e}")
|
|
28
|
+
return False
|
|
29
|
+
|
|
30
|
+
imdb_id = None
|
|
31
|
+
try:
|
|
32
|
+
imdb_link = soup.find('a', href=re.compile(r"imdb\.com/title/tt\d+", re.IGNORECASE))
|
|
33
|
+
if imdb_link:
|
|
34
|
+
href = imdb_link['href'].strip()
|
|
35
|
+
m = re.search(r"(tt\d{4,7})", href)
|
|
36
|
+
if m:
|
|
37
|
+
imdb_id = m.group(1)
|
|
38
|
+
else:
|
|
39
|
+
debug(f"{hostname}: imdb_id not found for title {title} in link href.")
|
|
40
|
+
else:
|
|
41
|
+
debug(f"{hostname}: imdb_id link href not found for title {title}.")
|
|
42
|
+
except Exception:
|
|
43
|
+
debug(f"{hostname}: failed to extract imdb_id for title {title}.")
|
|
44
|
+
|
|
45
|
+
anchors = []
|
|
46
|
+
for retries in range(10):
|
|
47
|
+
form = soup.find('form', id=re.compile(r'content-protector-access-form'))
|
|
48
|
+
if not form:
|
|
49
|
+
return False
|
|
50
|
+
|
|
51
|
+
action = form.get('action') or url
|
|
52
|
+
action_url = urljoin(resp.url, action)
|
|
53
|
+
|
|
54
|
+
payload = {}
|
|
55
|
+
for inp in form.find_all('input'):
|
|
56
|
+
name = inp.get('name')
|
|
57
|
+
if not name:
|
|
58
|
+
continue
|
|
59
|
+
value = inp.get('value', '')
|
|
60
|
+
payload[name] = value
|
|
61
|
+
|
|
62
|
+
append_patt = re.compile(r"append\(\s*[\'\"](?P<key>[^\'\"]+)[\'\"]\s*,\s*[\'\"](?P<val>[^\'\"]+)[\'\"]\s*\)",
|
|
63
|
+
re.IGNORECASE)
|
|
64
|
+
|
|
65
|
+
for script in soup.find_all('script'):
|
|
66
|
+
txt = script.string if script.string is not None else script.get_text()
|
|
67
|
+
if not txt:
|
|
68
|
+
continue
|
|
69
|
+
for m in append_patt.finditer(txt):
|
|
70
|
+
payload[m.group('key')] = m.group('val')
|
|
71
|
+
|
|
72
|
+
post_headers = headers.copy()
|
|
73
|
+
post_headers.update({'Referer': resp.url})
|
|
74
|
+
try:
|
|
75
|
+
resp = session.post(action_url, data=payload, headers=post_headers, timeout=30)
|
|
76
|
+
soup = BeautifulSoup(resp.text, 'html.parser')
|
|
77
|
+
except Exception as e:
|
|
78
|
+
info(f"{hostname}: could not submit protector form for {title}: {e}")
|
|
79
|
+
break
|
|
80
|
+
|
|
81
|
+
unlocked = soup.select('.content-protector-access-form')
|
|
82
|
+
if unlocked:
|
|
83
|
+
for u in unlocked:
|
|
84
|
+
anchors.extend(u.find_all('a', href=True))
|
|
85
|
+
|
|
86
|
+
if anchors:
|
|
87
|
+
break
|
|
88
|
+
|
|
89
|
+
links = []
|
|
90
|
+
for a in anchors:
|
|
91
|
+
try:
|
|
92
|
+
href = a['href'].strip()
|
|
93
|
+
|
|
94
|
+
netloc = urlparse(href).netloc
|
|
95
|
+
hoster = netloc.split(':')[0].lower()
|
|
96
|
+
parts = hoster.split('.')
|
|
97
|
+
if len(parts) >= 2:
|
|
98
|
+
hoster = parts[-2]
|
|
99
|
+
|
|
100
|
+
links.append([href, hoster])
|
|
101
|
+
except Exception:
|
|
102
|
+
debug(f"{hostname}: could not resolve download link hoster for {title}")
|
|
103
|
+
continue
|
|
104
|
+
|
|
105
|
+
if not links:
|
|
106
|
+
info(f"No external download links found on {hostname} page for {title}")
|
|
107
|
+
return False
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
"links": links,
|
|
111
|
+
"imdb_id": imdb_id,
|
|
112
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Quasarr
|
|
3
|
+
# Project by https://github.com/rix1337
|
|
4
|
+
|
|
5
|
+
import requests
|
|
6
|
+
from bs4 import BeautifulSoup
|
|
7
|
+
|
|
8
|
+
from quasarr.providers.log import info
|
|
9
|
+
|
|
10
|
+
hostname = "nk"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get_nk_download_links(shared_state, url, mirror, title):
|
|
14
|
+
host = shared_state.values["config"]("Hostnames").get(hostname)
|
|
15
|
+
headers = {
|
|
16
|
+
'User-Agent': shared_state.values["user_agent"],
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
session = requests.Session()
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
resp = session.get(url, headers=headers, timeout=20)
|
|
23
|
+
soup = BeautifulSoup(resp.text, 'html.parser')
|
|
24
|
+
except Exception as e:
|
|
25
|
+
info(f"{hostname}: could not fetch release page for {title}: {e}")
|
|
26
|
+
return False
|
|
27
|
+
|
|
28
|
+
anchors = soup.select('a.btn-orange')
|
|
29
|
+
candidates = []
|
|
30
|
+
for a in anchors:
|
|
31
|
+
|
|
32
|
+
href = a.get('href', '').strip()
|
|
33
|
+
hoster = href.split('/')[3].lower()
|
|
34
|
+
if not href.lower().startswith(('http://', 'https://')):
|
|
35
|
+
href = 'https://' + host + href
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
href = requests.head(href, headers=headers, allow_redirects=True, timeout=20).url
|
|
39
|
+
except Exception as e:
|
|
40
|
+
info(f"{hostname}: could not resolve download link for {title}: {e}")
|
|
41
|
+
continue
|
|
42
|
+
|
|
43
|
+
if hoster == 'ddl.to':
|
|
44
|
+
hoster = 'ddownload'
|
|
45
|
+
|
|
46
|
+
candidates.append([href, hoster])
|
|
47
|
+
|
|
48
|
+
if not candidates:
|
|
49
|
+
info(f"No external download links found on {hostname} page for {title}")
|
|
50
|
+
|
|
51
|
+
return candidates
|
|
@@ -9,6 +9,8 @@ dd = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAABCElE
|
|
|
9
9
|
dt = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAA4ElEQVR4nD2PsWrCUBSGv3ubBoq+QevQUhAXV6dMRcyTZBV8CCfxCTq4ZOmSJwg+giB4t16p1NCpSFECyY2n5EL7LeeD8/PDr5qmEa01+XrNz35PaAxFUXCrNQ/DIVophbWW19WKt+WSp16PYLvlvtNBHY8ESZLg6ppPaxmMRgymUz4WC577fR5nM3SapryMx0RRxPf5jDjHXRi21bQEWZZhjCGeTIiDgOvpxM3hQLXb+QAiImVZSlVV0tS1XEXkPY7laz5vX0K74h/n/KnyXNxm41351B+tKoVcLt5Vt8svgsSBPKnRQSAAAAAASUVORK5CYII='
|
|
10
10
|
dw = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAA70lEQVR4nAXBTUrDQBiA4febmfjbpqZUsFkJxS6UIq4EvYS46CUExRPkBL2HFM/gIdxIFq4Ff2JNia3JzBefR+bzuZ1Op+E+y/q6Fd/GSe8qijZH1lpfN00uADdZNt5NDh7GJ8dng2HKxvYOBtB6jctms/5S3ePpxeWklw69tZE1IqKqrIxT81U1d4PReCJJ338HXBVURCCokhelMZW467YTt9pi1rUntPCHYJ0ljiyubMJh0yJFuZSk26ENjufPFV0CCeAUfLEo8V7pRBHWOMJvBc5Q+xpnIf8pl+fvi1Lztw9zlO6T7sWw9jy9vPIPUL1kPErau3YAAAAASUVORK5CYII='
|
|
11
11
|
fx = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAABB0lEQVR4nB2LP0sCcQBA3/2Uzugu8E4IoT9cSYhUENFWYSA1FFTQ4hK5SENNDW19hoYcImhvaGgLguYgcIk0haLOE7wrNGzo5A4v9K3vPWknmwt0LYrrunieTygkAAm300EIQTg5nWB3e5O39w9s54t4fISEYfBZq/H4VETIsoxp1ftyJpWkR7lSZXFhnsFIhLBtOxR9n1KliqZFeX4po+saZ4VLTMtC9M61TJrTk2OarR9WV5bIpJfZ2ljH8zxEw3EovVaZm01xd//AsKpwfXNLLKaTmDSQ9vJHwWE+x3ezRbv9i6oqTBkTmLU65xdXSNn9g0CWB2jYDuNjo/y5Lqoy1A+63S7/GeVj+5KBt3UAAAAASUVORK5CYII='
|
|
12
|
+
nk = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAHYYAAB2GAV2iE4EAAAAZdEVYdFNvZnR3YXJlAEFkb2JlIEltYWdlUmVhZHlxyWU8AAADrmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4NCjx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NTc3MiwgMjAxNC8wMS8xMy0xOTo0NDowMCAgICAgICAgIj4NCgk8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPg0KCQk8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxNCAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo4RUJCOThFRkQ5RTcxMUU1QTcyN0EzQzFGN0YzNjcwNCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo4RUJCOThGMEQ5RTcxMUU1QTcyN0EzQzFGN0YzNjcwNCI+DQoJCQk8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo4RUJCOThFREQ5RTcxMUU1QTcyN0EzQzFGN0YzNjcwNCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo4RUJCOThFRUQ5RTcxMUU1QTcyN0EzQzFGN0YzNjcwNCIvPg0KCQk8L3JkZjpEZXNjcmlwdGlvbj4NCgkJPHJkZjpEZXNjcmlwdGlvbiB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+PHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj48L3JkZjpEZXNjcmlwdGlvbj48L3JkZjpSREY+DQo8L3g6eG1wbWV0YT4NCjw/eHBhY2tldCBlbmQ9J3cnPz7hg4LgAAAA3UlEQVQoU2NgIAAYGRgYGP7//8/4cHak44//jJfY//3T+S0vdpz/zXvZZ8+/yTIzMDAwhP490ir479ecx89/H2X69nH123vvn/1592HVy6+/rjNeyrPR+/jz927mf3+5v7Pz+vH8/DSXm51Z7NW3v8ec5p11Zfr44fOSv5+/MDB+/fqP4dOnoJ9fvjN++/TtE/P376IMDAwMzL5qYt8Y/v5+ws383+ndb8ajLP9+2T36xZ7O/f9HTIyhFCMjzLVLIw28Hv8ROarA8sL64Ve+wyocX+R//mOQQfIQdgAAAghnscY2ZXIAAAAASUVORK5CYII='
|
|
13
|
+
he = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAHYYAAB2GAV2iE4EAAACsSURBVChTY2CAgv///zPC2DA+uhh28IiBxZ5FQTWdS8tUgktOLZlHTq2eR05tAoeschxYwTsGpnpRVq6bDMxscxhYue4xsPM8ZBIULWVg57nAwM6uyPCPg0N+DwPD4zgGhnMyDAz32BgY7jAwMJQxMDCeMmdgCAeb8oeBIeEfA4PjMgaG3FQGhnlODIzHHBgY18UzMARjur6+numfuDj3f0lJLogA1DvoCmEAAJVNNF84URktAAAAAElFTkSuQmCC'
|
|
12
14
|
mb = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAABE0lEQVR4nAEIAff+AQAAAACHttAxAQ0Kjd7o7ToDAQH/GREOxcDT4HK+cErSAYy70DHs/AG6tNjoFNzn6gD5+fwAQjElAIE/JuqWxdZEBBwVD5RSIhsUnsTV/JBRNwT9/v4AjcDU/DsgOxbW6RqUArra5zf39vb/KRwTBKPN3gBMKhwA3unyBJHE2P/i7/U6Auvz+ACkzt4AaTkmAN/u9AARCQYAQysdALXW5ADl8fYAAi4bEMbD5vQBMxoR/CwYEACVxtkAfUMr/PoECAEZDwrFAiAPCmwpEAjq/gcKBAUDAQCQwtcAMSIZBDkbDukIBAJtAmU5KNIP//1F1ePrvScWD/hSLR/4uNXkvAHz9kZ+RS/T0LpzUht5tWAAAAAASUVORK5CYII='
|
|
13
15
|
nx = 'data:image/png;base64,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'
|
|
14
16
|
sf = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAA8ElEQVR4nBXBv0rDQADA4d/lz1lqmlREECUIFTq4FBeHDA6uPkRfwwfxBRx19gmk4GZBBAcjCAUrWEkaTUzucjnx+0R6kzQboZDCb7AWdNmiMo3KLHUmlCcDR/Z2DujvJXRth6kNqqgoXufo6kk6lpzwcEr7fUrx7POTugh9xPZkilE5jh9MsJ2mreaUi0eKdIa7CUIogjjB8wcnyOiM0rmkH+8ih8dY1gTxOeHoA69eLRCOxBvsE21JOmVo8gy3N6T+fMNbPdwiwzG6XFJ/vWC1xY9GvN9dsZxdI+4vxo3rtxLH8q/TFvPbYhqL0Z76AydWbEnTqqOqAAAAAElFTkSuQmCC'
|