Spaces:
Sleeping
Sleeping
| 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"<div style='margin-bottom: 15px; padding: 10px; background: #f0f8ff; border-radius: 5px;'>" | |
| stats_html += f"<strong>Stats:</strong> {working_count}/{total_count} IPs working " | |
| stats_html += f"({working_count/total_count*100:.1f}% success rate)" | |
| stats_html += "</div>" | |
| 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(""" | |
| <div class="header"> | |
| <h1 class="gradient-text" style="font-size: 2.5rem; font-weight: bold; margin-bottom: 0.5rem;"> | |
| 🔄 IP Swapper Wizard | |
| </h1> | |
| <p style="color: #6B7280; font-size: 1.1rem;"> | |
| Swap IP addresses in URLs while maintaining all parameters • Test Connectivity • Track History • Live Preview | |
| </p> | |
| </div> | |
| """) | |
| # Example Section | |
| with gr.Accordion("🎯 Real Example - Try This!", open=True): | |
| gr.Markdown(""" | |
| <div class="example-box"> | |
| <h3>🚀 Quick Start Example</h3> | |
| <p><strong>We've pre-loaded a real URL for you to test:</strong></p> | |
| </div> | |
| <div class="example-steps"> | |
| <h4>📝 Try This Example:</h4> | |
| <ol> | |
| <li><strong>URL is already loaded</strong> below (contains IP <code>51.158.55.104</code>)</li> | |
| <li><strong>Enter new IPs</strong> in either field: | |
| <ul> | |
| <li>Single IP: <code>192.168.1.100</code></li> | |
| <li>Multiple IPs (one per line): | |
| <pre>192.168.1.100 | |
| 10.0.0.50 | |
| 172.16.0.25</pre> | |
| </li> | |
| </ul> | |
| </li> | |
| <li><strong>Check "Test connectivity"</strong> to automatically test URLs</li> | |
| <li><strong>Click "Swap IP(s)"</strong> to generate and test new URLs</li> | |
| <li><strong>Test live</strong> using the iframe preview buttons</li> | |
| </ol> | |
| </div> | |
| <div style="background: #fffbeb; padding: 10px; border-radius: 5px; border-left: 4px solid #f59e0b;"> | |
| <strong>💡 What happens:</strong> The tool will replace <code>51.158.55.104</code> with your new IP(s) in both the main URL AND the encoded 'r' parameter! | |
| </div> | |
| """) | |
| 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="<div style='padding: 10px; background: #f3f4f6; border-radius: 5px; text-align: center;'>Generated URLs will appear here after processing</div>" | |
| ) | |
| with gr.Column(scale=7): | |
| iframe_component = gr.HTML( | |
| value=""" | |
| <div class="iframe-container"> | |
| <div class="iframe-header"> | |
| <span>🔍 Live Browser Preview</span> | |
| </div> | |
| <iframe src="about:blank" class="iframe-content" id="preview-iframe" | |
| sandbox="allow-scripts allow-same-origin allow-forms allow-popups" | |
| loading="lazy"></iframe> | |
| </div> | |
| """ | |
| ) | |
| # 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: "<div style='padding: 10px; background: #f3f4f6; border-radius: 5px; text-align: center;'>Use 'Swap IP(s)' to generate testable URLs</div>", | |
| 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: "<div style='padding: 10px; background: #f3f4f6; border-radius: 5px; text-align: center;'>Generated URLs will appear here after processing</div>", | |
| 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 "<div style='padding: 10px; background: #f3f4f6; border-radius: 5px; text-align: center;'>No valid URLs to test</div>" | |
| # 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 "<div style='padding: 10px; background: #f3f4f6; border-radius: 5px; text-align: center;'>No valid URLs found in output</div>" | |
| # Generate buttons HTML | |
| buttons_html = '<div class="url-test-buttons">' | |
| buttons_html += '<strong>Test URLs in Live Preview:</strong><br>' | |
| for i, (url, ip) in enumerate(zip(urls, ips)): | |
| button_id = f"url-btn-{i}" | |
| buttons_html += f''' | |
| <button class="url-test-btn" onclick="loadIframe('{url}', '{ip}')" style="margin: 2px; padding: 5px 10px; border-radius: 4px;"> | |
| 🌐 Test {ip} | |
| </button> | |
| ''' | |
| buttons_html += '</div>' | |
| buttons_html += ''' | |
| <script> | |
| function loadIframe(url, ip) { | |
| const iframe = document.getElementById('preview-iframe'); | |
| const title = document.querySelector('[data-testid="iframe_title"] input'); | |
| iframe.src = url; | |
| if (title) title.value = 'Testing: ' + ip; | |
| // Update Gradio state (optional) | |
| if (window.gradio_app) { | |
| // You can add Gradio state updates here if needed | |
| } | |
| } | |
| </script> | |
| ''' | |
| 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 | |
| ) |