Spaces:
Paused
Paused
| import streamlit as st | |
| import os | |
| import subprocess | |
| import shutil | |
| import zipfile | |
| import requests | |
| # Function to debug APK file | |
| def debug_apk(input_path, output_dir): | |
| # Run apk-mitm command | |
| command = f"apk-mitm {input_path} -o {output_dir}" | |
| result = subprocess.run(command, shell=True, capture_output=True, text=True) | |
| if result.returncode == 0: | |
| # Extract the patched file name from stdout | |
| output_file_name = result.stdout.split("Patched file: ")[-1].strip() | |
| output_path = os.path.join(output_dir, output_file_name) | |
| return output_path | |
| else: | |
| st.error(f"Error debugging APK: {result.stderr}") | |
| return None | |
| # Function to process XAPK file | |
| def process_xapk(xapk_path): | |
| """ | |
| Process XAPK file: rename to ZIP, extract contents, merge with APKEditor.jar, and sign the APK. | |
| Args: | |
| xapk_path (str): Path to the XAPK file | |
| Returns: | |
| str: Path to the signed APK if successful, None if failed | |
| """ | |
| try: | |
| # Get the directory and filename without extension | |
| folder = os.path.dirname(xapk_path) | |
| name_without_ext = os.path.splitext(os.path.basename(xapk_path))[0] | |
| # Create paths for zip and extraction | |
| zip_path = os.path.join(folder, f"{name_without_ext}.zip") | |
| extract_dir = os.path.join(folder, name_without_ext) | |
| # Rename XAPK to ZIP and extract contents | |
| shutil.move(xapk_path, zip_path) | |
| with zipfile.ZipFile(zip_path, 'r') as zip_ref: | |
| zip_ref.extractall(extract_dir) | |
| # Remove the ZIP file after extraction | |
| os.remove(zip_path) | |
| # Run APKEditor.jar command to merge the extracted files | |
| command = f'java -jar APKEditor.jar m -i "{extract_dir}"' | |
| result = subprocess.run(command, shell=True, capture_output=True, text=True) | |
| if result.returncode == 0: | |
| # Define the path for the merged APK | |
| merged_apk_path = os.path.join(folder, f"{name_without_ext}_merged.apk") | |
| st.write(f"Merged APK created at: {merged_apk_path}") | |
| # Proceed to sign the merged APK | |
| signed_apk_path = process_sign(merged_apk_path) | |
| return signed_apk_path | |
| else: | |
| st.error(f"Error merging APK: {result.stderr}") | |
| return None | |
| except Exception as e: | |
| st.error(f"Error processing XAPK: {str(e)}") | |
| # Clean up in case of error | |
| shutil.rmtree(extract_dir, ignore_errors=True) | |
| return None | |
| # Function to sign APK file | |
| def process_sign(apk_path): | |
| """ | |
| Sign the APK file using uber-apk-signer. | |
| Args: | |
| apk_path (str): Path to the APK file to be signed. | |
| Returns: | |
| str: Path to the signed APK if successful, None if failed. | |
| """ | |
| folder = os.path.dirname(apk_path) | |
| command = f"java -jar uber-apk-signer.jar --apks {apk_path}" | |
| result = subprocess.run(command, shell=True, capture_output=True, text=True) | |
| if result.returncode == 0: | |
| # Extract the path of the signed APK from the output directory | |
| signed_apk_path = apk_path.replace('.apk', '-aligned-debugSigned.apk') | |
| # Check if the signed APK exists at the expected path | |
| if os.path.exists(signed_apk_path): | |
| st.write("APK signing successful!") | |
| return signed_apk_path | |
| else: | |
| st.error("APK signing completed, but the signed file could not be found.") | |
| return None | |
| else: | |
| # Display the error message in a more user-friendly way | |
| st.error(f"Error signing APK: {result.stderr}") | |
| return None | |
| # Streamlit app interface | |
| st.title("APK File Processor") | |
| # File upload | |
| uploaded_file = st.file_uploader("Upload APK file", type=['apk', 'xapk', 'apks']) | |
| # URL upload | |
| url_input = st.text_input("Or enter APK URL") | |
| # Radio buttons to select the processing type | |
| processing_option = st.radio("Choose the processing type:", ('Process XAPK', 'Sign APK', 'Debug APK')) | |
| if uploaded_file is not None or url_input: | |
| # Create directories if they don't exist | |
| upload_dir = "uploads" | |
| if not os.path.exists(upload_dir): | |
| os.makedirs(upload_dir) | |
| if uploaded_file is not None: | |
| # Save uploaded file | |
| input_path = os.path.join(upload_dir, uploaded_file.name) | |
| # Write the uploaded file to the filesystem | |
| with open(input_path, "wb") as f: | |
| f.write(uploaded_file.read()) | |
| # Now that the file is saved, rename it if it contains spaces | |
| if " " in uploaded_file.name: | |
| new_name = uploaded_file.name.replace(" ", "_") # Replace spaces with underscores | |
| new_input_path = os.path.join(upload_dir, new_name) | |
| # Only rename if the new name is different | |
| if new_input_path != input_path: | |
| os.rename(input_path, new_input_path) | |
| input_path = new_input_path # Update input_path to new name | |
| elif url_input: | |
| # Download APK from URL | |
| st.write("Downloading APK from URL...") | |
| response = requests.get(url_input) | |
| if response.status_code == 200: | |
| input_path = os.path.join(upload_dir, os.path.basename(url_input)) | |
| with open(input_path, "wb") as f: | |
| f.write(response.content) | |
| else: | |
| st.error("Failed to download APK from URL. Please check the URL and try again.") | |
| st.stop() | |
| # Define output directory for the processed APK | |
| output_dir = upload_dir | |
| # Execute the process based on user choice | |
| if processing_option == 'Process XAPK' and input_path.endswith('.xapk'): | |
| st.write("Processing XAPK...") | |
| output_path = process_xapk(input_path) | |
| elif processing_option == 'Sign APK' and input_path.endswith('.apk'): | |
| st.write("Signing APK...") | |
| output_path = process_sign(input_path) | |
| elif processing_option == 'Debug APK' and input_path.endswith('.apk'): | |
| st.write("Debugging APK...") | |
| output_path = debug_apk(input_path, output_dir) | |
| else: | |
| st.error("The selected process is not compatible with the uploaded file type. Please ensure the file type matches your selection.") | |
| output_path = None | |
| # Handle the result of processing | |
| if output_path: | |
| st.success("APK processed successfully!") | |
| st.write("Output path:") | |
| st.text(output_path) | |
| # Provide download link for the processed APK file | |
| if os.path.exists(output_path): | |
| with open(output_path, "rb") as f: | |
| file_data = f.read() | |
| st.download_button( | |
| label="Download Processed APK", | |
| data=file_data, | |
| file_name=os.path.basename(output_path), | |
| mime="application/vnd.android.package-archive" | |
| ) | |
| else: | |
| st.error("Processed APK file not found. Please try again.") | |
| else: | |
| st.error("Failed to process APK. Please try again.") | |
| # Clean up the uploaded and processed files | |
| def cleanup_files(): | |
| shutil.rmtree(upload_dir, ignore_errors=True) | |
| # Cleanup is done after download button appears to ensure the file is available for download | |
| cleanup_files() | |