import gradio as gr import base64 from urllib.parse import urlparse, urlunparse, parse_qs, urlencode import re import requests import json import time from datetime import datetime import pandas as pd # Storage for history (in production, use a database) history_data = [] TEST_TIMEOUT = 10 # seconds def base64_url_decode(encoded_str): """Decode URL-safe base64 string""" encoded_str = encoded_str.replace('_', '/').replace('-', '+') padding = len(encoded_str) % 4 if padding: encoded_str += '=' * (4 - padding) try: decoded_bytes = base64.b64decode(encoded_str) return decoded_bytes.decode('utf-8') except Exception as e: raise ValueError(f"Base64 decoding error: {str(e)}") def base64_url_encode(decoded_str): """Encode string to URL-safe base64""" encoded_bytes = base64.b64encode(decoded_str.encode('utf-8')) encoded_str = encoded_bytes.decode('utf-8') return encoded_str.replace('/', '_').replace('+', '-').rstrip('=') def is_valid_ip(ip): """Validate IP address format""" ip_pattern = r'^(\d{1,3}\.){3}\d{1,3}$' if not re.match(ip_pattern, ip): return False parts = ip.split('.') for part in parts: if not 0 <= int(part) <= 255: return False return True def extract_ip_from_url(url): """Extract IP address from URL""" try: parsed = urlparse(url) return parsed.hostname except: return None def test_url_connectivity(url): """Test if URL is accessible""" try: headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' } response = requests.get(url, timeout=TEST_TIMEOUT, headers=headers, verify=False) status_info = { 'status': 'working' if response.status_code == 200 else 'not_working', 'status_code': response.status_code, 'response_time': response.elapsed.total_seconds(), 'timestamp': datetime.now().isoformat() } return status_info except requests.exceptions.RequestException as e: return { 'status': 'not_working', 'status_code': None, 'error': str(e), 'response_time': None, 'timestamp': datetime.now().isoformat() } def process_single_url(original_url, new_ip): """Process a single URL with IP replacement""" try: parsed = urlparse(original_url) old_ip = parsed.hostname # Replace IP in main URL new_netloc = new_ip if parsed.port: new_netloc += f":{parsed.port}" # Parse query parameters query_params = parse_qs(parsed.query) # Process 'r' parameter if it exists if 'r' in query_params: try: r_param = query_params['r'][0] decoded_r = base64_url_decode(r_param) # Replace IP in decoded URL decoded_parsed = urlparse(decoded_r) decoded_new_netloc = new_ip if decoded_parsed.port: decoded_new_netloc += f":{decoded_parsed.port}" new_decoded_url = decoded_parsed._replace(netloc=decoded_new_netloc).geturl() new_encoded_r = base64_url_encode(new_decoded_url) query_params['r'] = [new_encoded_r] except Exception as e: print(f"Note: Could not process 'r' parameter: {e}") # Rebuild query string new_query = urlencode(query_params, doseq=True) # Construct final URL new_url = parsed._replace(netloc=new_netloc, query=new_query).geturl() return new_url, old_ip except Exception as e: raise ValueError(f"Error processing URL: {str(e)}") def decode_r_parameter(url): """Decode the r parameter from URL""" try: parsed = urlparse(url) query_params = parse_qs(parsed.query) if 'r' in query_params: r_param = query_params['r'][0] decoded_r = base64_url_decode(r_param) return decoded_r else: return "No 'r' parameter found in URL" except Exception as e: return f"Error decoding 'r' parameter: {str(e)}" def ip_swapper_handler(original_url, single_ip, multiple_ips, test_connectivity): """Main handler function for IP swapping""" try: if not original_url.strip(): return "Please enter a URL", "", "", gr.update(visible=False), "about:blank", "No URL to display" # Extract original IP for display original_ip = extract_ip_from_url(original_url) # Process IP inputs ips_to_process = [] if single_ip.strip(): if not is_valid_ip(single_ip): return f"Error: Invalid IP address format: {single_ip}", "", "", gr.update(visible=False), "about:blank", "Invalid IP format" ips_to_process.append(single_ip) if multiple_ips.strip(): ip_list = [ip.strip() for ip in multiple_ips.split('\n') if ip.strip()] for ip in ip_list: if not is_valid_ip(ip): return f"Error: Invalid IP address format: {ip}", "", "", gr.update(visible=False), "about:blank", f"Invalid IP: {ip}" ips_to_process.extend(ip_list) if not ips_to_process: return "Please enter at least one valid IP address", "", "", gr.update(visible=False), "about:blank", "No IPs provided" # Process URLs results = [] if len(ips_to_process) == 1: new_url, old_ip = process_single_url(original_url, ips_to_process[0]) results.append({ 'ip': ips_to_process[0], 'url': new_url, 'original_url': original_url }) result_text = new_url iframe_url = new_url iframe_title = f"Testing: {ips_to_process[0]}" else: result_lines = [] for i, ip in enumerate(ips_to_process, 1): new_url, old_ip = process_single_url(original_url, ip) results.append({ 'ip': ip, 'url': new_url, 'original_url': original_url }) result_lines.append(f"Option {i} (IP: {ip}):") result_lines.append(new_url) result_lines.append("") result_text = "\n".join(result_lines) # For multiple IPs, show the first one in iframe iframe_url = results[0]['url'] if results else "about:blank" iframe_title = f"Testing: {results[0]['ip']}" if results else "Multiple IPs" # Get decoded r parameter decoded_r = decode_r_parameter(original_url) # Test connectivity if requested test_results = [] if test_connectivity and results: progress = gr.Progress() progress(0, desc="Testing URLs...") for i, result in enumerate(results): progress(i / len(results), desc=f"Testing {result['ip']}...") test_result = test_url_connectivity(result['url']) # Add to history history_entry = { 'timestamp': datetime.now().isoformat(), 'original_url': original_url, 'ip': result['ip'], 'modified_url': result['url'], 'status': test_result['status'], 'status_code': test_result.get('status_code'), 'response_time': test_result.get('response_time'), 'error': test_result.get('error') } history_data.append(history_entry) test_results.append({ 'ip': result['ip'], 'url': result['url'], 'status': test_result['status'], 'status_code': test_result.get('status_code'), 'response_time': test_result.get('response_time') }) # Update result text with status if len(test_results) == 1: status = "✅ WORKING" if test_results[0]['status'] == 'working' else "❌ NOT WORKING" result_text = f"{result_text}\n\n---\nStatus: {status}" if test_results[0].get('status_code'): result_text += f" (HTTP {test_results[0]['status_code']})" if test_results[0].get('response_time'): result_text += f" - Response time: {test_results[0]['response_time']:.2f}s" else: result_lines = [] for test in test_results: status = "✅ WORKING" if test['status'] == 'working' else "❌ NOT WORKING" status_info = f" - {status}" if test.get('status_code'): status_info += f" (HTTP {test['status_code']})" if test.get('response_time'): status_info += f" - {test['response_time']:.2f}s" result_lines.append(f"Option - IP: {test['ip']}{status_info}") result_lines.append(test['url']) result_lines.append("") result_text = "\n".join(result_lines) # Show history button if we have test results history_visible = len(test_results) > 0 return original_url, result_text, decoded_r, gr.update(visible=history_visible), iframe_url, iframe_title except Exception as e: return f"Error: {str(e)}", "", "", gr.update(visible=False), "about:blank", f"Error: {str(e)}" def get_history_df(): """Convert history data to DataFrame for display""" if not history_data: return pd.DataFrame(columns=['Timestamp', 'IP', 'Status', 'HTTP Code', 'Response Time', 'Error']) df_data = [] for entry in history_data[-50:]: # Show last 50 entries df_data.append({ 'Timestamp': entry['timestamp'][11:19], # Show only time 'IP': entry['ip'], 'Status': '✅ Working' if entry['status'] == 'working' else '❌ Not Working', 'HTTP Code': entry.get('status_code', 'N/A'), 'Response Time': f"{entry.get('response_time', 0):.2f}s" if entry.get('response_time') else 'N/A', 'Error': entry.get('error', '') }) return pd.DataFrame(df_data) def clear_all(): """Clear all inputs and outputs""" return "", "", "", "", "", gr.update(visible=False), "about:blank", "Ready for testing..." def show_history(): """Display history in a DataFrame""" history_df = get_history_df() if history_df.empty: return "No history data available. Test some URLs first!" # Count stats working_count = len([h for h in history_data if h['status'] == 'working']) total_count = len(history_data) stats_html = f"
" stats_html += f"Stats: {working_count}/{total_count} IPs working " stats_html += f"({working_count/total_count*100:.1f}% success rate)" stats_html += "
" return stats_html + history_df.to_html(classes='history-table', index=False, escape=False) def export_history(): """Export history as CSV""" if not history_data: return None df = pd.DataFrame(history_data) timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") filename = f"ip_swapper_history_{timestamp}.csv" df.to_csv(filename, index=False) return filename def load_url_in_iframe(url, ip_address): """Load a specific URL in the iframe""" if not url or url.startswith("Error:") or url.startswith("Please enter"): return "about:blank", "No valid URL to display" # Validate URL format try: parsed = urlparse(url) if parsed.scheme and parsed.netloc: return url, f"Testing: {ip_address}" else: return "about:blank", "Invalid URL format" except: return "about:blank", "Invalid URL" # Custom CSS for better styling custom_css = """ .gradio-container { font-family: 'Inter', sans-serif; } .header { text-align: center; margin-bottom: 2rem; } .gradient-text { background: linear-gradient(90deg, #8B5CF6, #EC4899, #EF4444); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; } .code-block { font-family: 'Courier New', monospace; background-color: #1E293B; color: #E2E8F0; border-radius: 0.5rem; padding: 1rem; white-space: pre-wrap; word-break: break-all; } .history-table { width: 100%; border-collapse: collapse; } .history-table th, .history-table td { border: 1px solid #ddd; padding: 8px; text-align: left; } .history-table th { background-color: #4f46e5; color: white; } .history-table tr:nth-child(even) { background-color: #f2f2f2; } .status-working { color: #10b981; font-weight: bold; } .status-not-working { color: #ef4444; font-weight: bold; } .test-btn { background: linear-gradient(90deg, #10b981, #059669) !important; } .iframe-container { border: 2px solid #e5e7eb; border-radius: 10px; overflow: hidden; margin-top: 10px; } .iframe-header { background: #4f46e5; color: white; padding: 8px 12px; font-weight: bold; display: flex; justify-content: between; align-items: center; } .iframe-content { height: 500px; width: 100%; } .url-test-buttons { margin: 10px 0; display: flex; gap: 10px; flex-wrap: wrap; } .url-test-btn { background: #4f46e5 !important; color: white !important; border: none !important; } .url-test-btn:hover { background: #4338ca !important; } .example-box { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 15px; border-radius: 10px; margin: 10px 0; } .example-steps { background: #f8f9fa; border-left: 4px solid #4f46e5; padding: 15px; margin: 15px 0; border-radius: 0 8px 8px 0; } """ # Create Gradio interface with gr.Blocks(css=custom_css, theme=gr.themes.Soft()) as demo: # Header gr.HTML("""

🔄 IP Swapper Wizard

Swap IP addresses in URLs while maintaining all parameters • Test Connectivity • Track History • Live Preview

""") # Example Section with gr.Accordion("🎯 Real Example - Try This!", open=True): gr.Markdown("""

🚀 Quick Start Example

We've pre-loaded a real URL for you to test:

📝 Try This Example:

  1. URL is already loaded below (contains IP 51.158.55.104)
  2. Enter new IPs in either field:
    • Single IP: 192.168.1.100
    • Multiple IPs (one per line):
      192.168.1.100
      10.0.0.50
      172.16.0.25
  3. Check "Test connectivity" to automatically test URLs
  4. Click "Swap IP(s)" to generate and test new URLs
  5. Test live using the iframe preview buttons
💡 What happens: The tool will replace 51.158.55.104 with your new IP(s) in both the main URL AND the encoded 'r' parameter!
""") with gr.Row(): with gr.Column(scale=1): # Input Section with gr.Group(): gr.Markdown("### 📥 Input Parameters") url_input = gr.Textbox( label="Enter your URL:", placeholder="Paste URL here...", value="https://51.158.55.104/__cpi.php?s=UkQ2YXlSaWJuc3ZoeGR2dG04WW9LamZUQTBwaG1wTzM1aCtMajhkTXRaV1BwQ3lCSG0vWmxUV2lkVDVlc2pveHFuM0RaY0ZDaUgwNzdkMnFMWVJnY1pzSFNXYXB6c1EzRXpqVFViNzBqSlE9&r=aHR0cHM6Ly81MS4xNTguNTUuMTA0L3B0cC8%2FdXNlcj1wbGF0Zm9ybXNpbmNvbWUmc3ViaWQ9MTMxMzcwJl9fY3BvPWFIUjBjSE02THk5aFpHNWhaR1V1Ym1WMA%3D%3D&__cpo=1", lines=3 ) single_ip_input = gr.Textbox( label="Single IP Address:", placeholder="e.g., 192.168.1.100", max_lines=1 ) multiple_ips_input = gr.Textbox( label="Multiple IP Addresses:", placeholder="Enter one IP per line\n192.168.1.100\n10.0.0.50\n172.16.0.25", lines=4 ) test_connectivity = gr.Checkbox( label="🔍 Test URL connectivity after swapping", value=True, info="Test if the modified URLs are accessible" ) # Action Buttons with gr.Row(): process_btn = gr.Button("🔄 Swap IP(s)", variant="primary", size="lg") clear_btn = gr.Button("🗑️ Clear All", variant="secondary") decode_btn = gr.Button("🔍 Decode Only", variant="secondary") with gr.Column(scale=1): # Output Section with gr.Group(): gr.Markdown("### 📤 Results") original_url_output = gr.Textbox( label="Original URL:", interactive=False, lines=2 ) modified_url_output = gr.Textbox( label="Modified URL(s):", interactive=False, lines=4 ) decoded_r_output = gr.Textbox( label="Decoded 'r' Parameter:", interactive=False, lines=3 ) # Hidden history button that becomes visible after testing history_btn = gr.Button( "📊 Show Test History", visible=False, variant="secondary" ) # Live Preview Section with gr.Accordion("🌐 Live URL Preview & Testing", open=True): gr.Markdown("**Test your generated URLs directly in the browser below:**") with gr.Row(): with gr.Column(scale=3): iframe_title = gr.Textbox( label="Current Preview:", interactive=False, max_lines=1, value="Ready for testing..." ) # URL testing buttons will be dynamically generated url_test_buttons = gr.HTML( value="
Generated URLs will appear here after processing
" ) with gr.Column(scale=7): iframe_component = gr.HTML( value="""
🔍 Live Browser Preview
""" ) # History Section with gr.Accordion("📊 Test History & Analytics", open=False): with gr.Row(): refresh_history_btn = gr.Button("🔄 Refresh History", size="sm") export_history_btn = gr.Button("📤 Export History as CSV", size="sm") clear_history_btn = gr.Button("🗑️ Clear History", size="sm", variant="stop") history_output = gr.HTML(label="Test History") # Instructions with gr.Accordion("📖 How It Works", open=False): gr.Markdown(""" ## 🎯 Real Example Walkthrough **Original URL Analysis:** ``` https://51.158.55.104/__cpi.php?s=...&r=aHR0cHM6Ly81MS4xNTguNTUuMTA0L3B0cC8... ``` **What the tool does:** 1. **Extracts main IP**: `51.158.55.104` 2. **Decodes 'r' parameter**: Contains another URL with the same IP 3. **Replaces IP in both places** with your new IP(s) 4. **Re-encodes 'r' parameter** with new IP 5. **Generates final URL** with swapped IPs **Step-by-step process:** 1. **Paste your URL** containing IP addresses 2. **Enter IP address(es)** - single or multiple (one per line) 3. **Check 'Test connectivity'** to automatically test URLs 4. **Click 'Swap IP(s)'** to process and test 5. **View results** with status indicators (✅ Working / ❌ Not Working) 6. **Test URLs live** in the built-in browser 7. **Check history** for past test results **Live Preview Features:** - Test URLs directly in the app - Built-in browser with iframe - Quick navigation between multiple IPs - Safe sandboxed environment **Status Indicators:** - ✅ Working: URL responded with HTTP 200 - ❌ Not Working: Timeout, error, or non-200 response """) # Event handlers process_btn.click( fn=ip_swapper_handler, inputs=[url_input, single_ip_input, multiple_ips_input, test_connectivity], outputs=[original_url_output, modified_url_output, decoded_r_output, history_btn, iframe_component, iframe_title] ).then( fn=lambda modified_url, single_ip, multiple_ips: generate_url_buttons(modified_url, single_ip, multiple_ips), inputs=[modified_url_output, single_ip_input, multiple_ips_input], outputs=[url_test_buttons] ) decode_btn.click( fn=lambda url: (url, "", decode_r_parameter(url), gr.update(visible=False), "about:blank", "Decode Only Mode") if url else ("", "", "Please enter a URL", gr.update(visible=False), "about:blank", "No URL"), inputs=[url_input], outputs=[original_url_output, modified_url_output, decoded_r_output, history_btn, iframe_component, iframe_title] ).then( fn=lambda modified_url, single_ip, multiple_ips: "
Use 'Swap IP(s)' to generate testable URLs
", inputs=[modified_url_output, single_ip_input, multiple_ips_input], outputs=[url_test_buttons] ) clear_btn.click( fn=clear_all, inputs=[], outputs=[url_input, single_ip_input, multiple_ips_input, original_url_output, modified_url_output, decoded_r_output, history_btn, iframe_title] ).then( fn=lambda: "about:blank", inputs=[], outputs=[iframe_component] ).then( fn=lambda: "
Generated URLs will appear here after processing
", inputs=[], outputs=[url_test_buttons] ) # History handlers history_btn.click( fn=show_history, inputs=[], outputs=[history_output] ) refresh_history_btn.click( fn=show_history, inputs=[], outputs=[history_output] ) export_history_btn.click( fn=export_history, inputs=[], outputs=gr.File(label="Download History CSV") ) clear_history_btn.click( fn=lambda: (history_data.clear(), "History cleared!"), inputs=[], outputs=[history_output] ) def generate_url_buttons(modified_url, single_ip, multiple_ips): """Generate clickable buttons for testing URLs in iframe""" if not modified_url or modified_url.startswith("Error:") or modified_url.startswith("Please enter"): return "
No valid URLs to test
" # Extract URLs from the modified_url text urls = [] ips = [] if single_ip and is_valid_ip(single_ip): # Single IP case - extract the URL directly urls.append(modified_url.split('\n')[0]) # Take first line as URL ips.append(single_ip) if multiple_ips: # Multiple IPs case - parse the formatted output ip_list = [ip.strip() for ip in multiple_ips.split('\n') if ip.strip() and is_valid_ip(ip)] lines = modified_url.split('\n') for i, line in enumerate(lines): if line.startswith('http'): # Find the corresponding IP for ip in ip_list: if ip in lines[i-1] if i > 0 else False: urls.append(line) ips.append(ip) break # If we couldn't parse structured output, try to extract URLs directly if not urls: import re url_pattern = r'https?://[^\s]+' found_urls = re.findall(url_pattern, modified_url) urls = found_urls # Use the provided IPs or generate placeholders if single_ip: ips = [single_ip] * len(urls) elif multiple_ips: ip_list = [ip.strip() for ip in multiple_ips.split('\n') if ip.strip()] ips = ip_list[:len(urls)] else: ips = [f"IP_{i+1}" for i in range(len(urls))] if not urls: return "
No valid URLs found in output
" # Generate buttons HTML buttons_html = '
' buttons_html += 'Test URLs in Live Preview:
' for i, (url, ip) in enumerate(zip(urls, ips)): button_id = f"url-btn-{i}" buttons_html += f''' ''' buttons_html += '
' buttons_html += ''' ''' return buttons_html # Launch configuration if __name__ == "__main__": # Disable SSL warnings for testing import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) demo.launch( server_name="0.0.0.0", server_port=7860, share=True, show_error=True )