previewteam2.1 / app.py
Sjwjne's picture
Update app.py
d63be2c verified
Raw
History Blame Contribute Delete
5.43 kB
import os, zipfile, subprocess, shutil, uuid, requests, traceback
from flask import Flask, request, jsonify, send_file
from flask_cors import CORS
app = Flask(__name__)
# Enable CORS for external HTML Requests!
CORS(app)
def generate_keystore(work_dir):
keystore_path = os.path.abspath(os.path.join(work_dir, "debug.keystore"))
cmd = [
"keytool", "-genkey", "-v", "-keystore", keystore_path,
"-storepass", "android", "-alias", "androiddebugkey", "-keypass", "android",
"-keyalg", "RSA", "-keysize", "2048", "-validity", "10000",
"-dname", "CN=AndroidDebug,O=Android,C=US"
]
subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
return keystore_path
def push_to_appetize(apk_path, api_token):
""" Upload to appetize platform to stream App inside the web lightweight layer """
url = "https://api.appetize.io/v1/apps"
try:
with open(apk_path, "rb") as app_file:
files = {"file": app_file}
headers = {"Authorization": f"Bearer {api_token}"}
# Data stream params config
data = { "platform": "android" }
resp = requests.post(url, headers=headers, files=files, data=data)
if resp.status_code == 200:
json_data = resp.json()
public_key = json_data.get("publicKey", "")
return f"https://appetize.io/embed/{public_key}?device=pixel7&scale=80&autoplay=true&language=en"
else:
return f"API_REJECTED: Status Code {resp.status_code}. Response: {resp.text}"
except Exception as e:
return f"REQUEST_FAILED: {str(e)}"
@app.route('/', methods=['GET'])
def index():
try:
return send_file('index.html')
except Exception as e:
return f"Index File load failed: {str(e)}", 500
@app.route('/upload_project', methods=['POST'])
def build_and_preview_app():
# Hugging Face Settings/Secrets Fetcher API variable
APPETIZE_API_KEY = os.getenv("APPETIZE_API_KEY", "").strip()
if not APPETIZE_API_KEY:
return jsonify({'success': False, 'message': 'System Config Error: Hugging Face space variables missing (APPETIZE_API_KEY). Go to space settings and add it!'})
if 'file' not in request.files:
return jsonify({'success': False, 'message': 'No zip file sent with request!'})
file = request.files['file']
run_id = str(uuid.uuid4())[:8]
work_dir = os.path.abspath(f"build_{run_id}")
os.makedirs(work_dir, exist_ok=True)
zip_location = os.path.join(work_dir, "android_code.zip")
extract_path = os.path.join(work_dir, "src")
try:
file.save(zip_location)
with zipfile.ZipFile(zip_location, "r") as zip_ref:
zip_ref.extractall(extract_path)
project_folder = next((root for root, _, files in os.walk(extract_path) if "gradlew" in files), None)
if not project_folder:
return jsonify({'success': False, 'message': 'Missing build code folder, (gradlew format mismatch). Check Zip Content!'})
subprocess.run(["chmod", "+x", "gradlew"], cwd=project_folder)
result = subprocess.run(["./gradlew", "assembleRelease", "--no-daemon", "--stacktrace"],
cwd=project_folder, capture_output=True, text=True)
if result.returncode == 0:
final_unsigned_apk = next((os.path.join(root, fi)
for root, _, files in os.walk(project_folder)
for fi in files if fi.endswith(".apk") and "release" in fi), None)
if final_unsigned_apk:
ks = generate_keystore(work_dir)
apk_done = os.path.join(work_dir, f"Final_App_{run_id}.apk")
subprocess.run(["apksigner", "sign", "--ks", ks, "--ks-pass", "pass:android", "--out", apk_done, final_unsigned_apk])
# Request Live url Engine Logic!
api_response_url = push_to_appetize(apk_done, APPETIZE_API_KEY)
if api_response_url.startswith("http"):
return jsonify({'success': True, 'preview_url': api_response_url})
else:
return jsonify({'success': False, 'message': f'Server processed well but Live App Connection failed. Message details from Appetaize: {api_response_url}'})
else:
return jsonify({'success': False, 'message': 'Folder execution built success! However system fails identifying output extension \'.apk\''})
return jsonify({'success': False, 'message': f"Syntax source script syntax/library compile Gradle break:\n\n{result.stderr[-500:]}"})
except Exception as e:
# Here Python completely grabs crashes making no screen turns 500 html format errors rather it sends precise data logs inside Response!!
stacktrace_issue = traceback.format_exc()
print("INTERNAL BUG EXPOSED:\n" + stacktrace_issue)
return jsonify({'success': False, 'message': f"Server Backend Fatal Alert: {str(e)} \nView Server Logs."})
finally:
# House Keeping Data
if os.path.exists(work_dir):
try: shutil.rmtree(work_dir)
except: pass
if __name__ == '__main__':
# Launch system port standard map rule !
app.run(host='0.0.0.0', port=7860)