# app.py from flask import Flask, send_file, request, jsonify, Response import io, os, random import threading import time import gradio as gr # —————————————————————————————— # Minimal Flask backend for LibreSpeed-style tests # —————————————————————————————— app = Flask(__name__) # Pre-generate a 10 MiB random blob for download tests DOWNLOAD_SIZE_BYTES = 10 * 1024 * 1024 # 10 MiB # We generate once at startup to avoid repeated overhead _random_blob = os.urandom(DOWNLOAD_SIZE_BYTES) @app.route("/download_test") def download_test(): """ Serve a fixed-size blob (10 MiB) to the client for download‐speed measurement. """ return Response( _random_blob, mimetype="application/octet-stream", headers={ "Content-Disposition": f"attachment; filename=\"test_{DOWNLOAD_SIZE_BYTES}.bin\"" } ) @app.route("/upload_test", methods=["POST"]) def upload_test(): """ Accept arbitrary data from client. Return immediately with length for timing. """ data = request.get_data() size = len(data) # bytes received # We don’t store it; this endpoint exists purely to let the JS measure upload speed. return jsonify({"received_bytes": size}) # —————————————————————————————— # HTML/JS frontend (embedded LibreSpeed logic) # —————————————————————————————— LIBRESPEED_HTML = """
This test will measure your real Internet speed by downloading a 10 MiB file and uploading random data.
""" # Expose the HTML through Gradio’s Interface def serve_html(): return LIBRESPEED_HTML iface = gr.Interface( fn=serve_html, inputs=[], outputs=gr.HTML(label="Speed Test"), title="Client-Side Speed Test", description="Measures your actual Internet link (download/upload/ping) via JavaScript." ) def gradio_thread(): """ Run Gradio in a thread so that Flask and Gradio both serve on the same port. """ iface.launch(server_name="0.0.0.0", server_port=7860, share=False) if __name__ == "__main__": # 1) Start Gradio in a background thread t = threading.Thread(target=gradio_thread, daemon=True) t.start() # 2) Start Flask to serve endpoints (on port 7860 as well) # Since Gradio’s Flask is already bound, we just add our routes to the same server. # Gradio uses Flask under the hood, so these routes will be served automatically. # (In recent Gradio versions, custom @app routes work if we set `allow_flagging="never"`, etc.) app.run(host="0.0.0.0", port=7860, debug=False)