euler314 commited on
Commit
5f018aa
Β·
verified Β·
1 Parent(s): 4c0dc07

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +153 -23
app.py CHANGED
@@ -1,37 +1,167 @@
1
  # app.py
2
 
3
- import speedtest
 
 
 
4
  import gradio as gr
5
 
6
- def run_speedtest():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  """
8
- Runs a speed test and returns download speed (Mbps),
9
- upload speed (Mbps), and ping (ms).
 
 
 
 
 
 
 
 
10
  """
11
- st = speedtest.Speedtest()
12
- st.get_best_server()
13
- download_bps = st.download()
14
- upload_bps = st.upload()
15
- ping_ms = st.results.ping
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
- # Convert from bits per second to megabits per second
18
- download_mbps = round(download_bps / 1e6, 2)
19
- upload_mbps = round(upload_bps / 1e6, 2)
 
20
 
21
- return f"{download_mbps} Mbps", f"{upload_mbps} Mbps", f"{ping_ms:.2f} ms"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
- # Build Gradio interface
24
  iface = gr.Interface(
25
- fn=run_speedtest,
26
  inputs=[],
27
- outputs=[
28
- gr.Textbox(label="Download Speed"),
29
- gr.Textbox(label="Upload Speed"),
30
- gr.Textbox(label="Ping")
31
- ],
32
- title="Wi-Fi / Ethernet Speed Test",
33
- description="Click **Run Speed Test** to measure your current Internet download/upload speeds and ping."
34
  )
35
 
 
 
 
 
 
 
36
  if __name__ == "__main__":
37
- iface.launch(server_name="0.0.0.0", server_port=7860)
 
 
 
 
 
 
 
 
 
1
  # app.py
2
 
3
+ from flask import Flask, send_file, request, jsonify, Response
4
+ import io, os, random
5
+ import threading
6
+ import time
7
  import gradio as gr
8
 
9
+ # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
10
+ # Minimal Flask backend for LibreSpeed-style tests
11
+ # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
12
+
13
+ app = Flask(__name__)
14
+
15
+ # Pre-generate a 10 MiB random blob for download tests
16
+ DOWNLOAD_SIZE_BYTES = 10 * 1024 * 1024 # 10 MiB
17
+
18
+ # We generate once at startup to avoid repeated overhead
19
+ _random_blob = os.urandom(DOWNLOAD_SIZE_BYTES)
20
+
21
+ @app.route("/download_test")
22
+ def download_test():
23
+ """
24
+ Serve a fixed-size blob (10 MiB) to the client for download‐speed measurement.
25
  """
26
+ return Response(
27
+ _random_blob,
28
+ mimetype="application/octet-stream",
29
+ headers={
30
+ "Content-Disposition": f"attachment; filename=\"test_{DOWNLOAD_SIZE_BYTES}.bin\""
31
+ }
32
+ )
33
+
34
+ @app.route("/upload_test", methods=["POST"])
35
+ def upload_test():
36
  """
37
+ Accept arbitrary data from client. Return immediately with length for timing.
38
+ """
39
+ data = request.get_data()
40
+ size = len(data) # bytes received
41
+ # We don’t store it; this endpoint exists purely to let the JS measure upload speed.
42
+ return jsonify({"received_bytes": size})
43
+
44
+ # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
45
+ # HTML/JS frontend (embedded LibreSpeed logic)
46
+ # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
47
+
48
+ LIBRESPEED_HTML = """
49
+ <!DOCTYPE html>
50
+ <html lang="en">
51
+ <head>
52
+ <meta charset="UTF-8" />
53
+ <title>Client-Side Speed Test</title>
54
+ <style>
55
+ body { font-family: sans-serif; max-width: 600px; margin: 2em auto; }
56
+ button { padding: 0.5em 1em; font-size: 1rem; }
57
+ .result { margin-top: 1.5em; }
58
+ .label { font-weight: bold; }
59
+ </style>
60
+ </head>
61
+ <body>
62
+ <h2>Wi-Fi / Ethernet Speed Test (Client-Side)</h2>
63
+ <p>This test will measure your real Internet speed by downloading a 10 MiB file and uploading random data.</p>
64
+ <button id="startBtn">Start Speed Test</button>
65
+
66
+ <div class="result" id="results" style="display: none;">
67
+ <p><span class="label">Download Speed:</span> <span id="dlSpeed">–</span></p>
68
+ <p><span class="label">Upload Speed:</span> <span id="ulSpeed">–</span></p>
69
+ <p><span class="label">Ping:</span> <span id="ping">–</span></p>
70
+ </div>
71
+
72
+ <script>
73
+ const startBtn = document.getElementById("startBtn");
74
+ const resultsDiv = document.getElementById("results");
75
+ const dlSpan = document.getElementById("dlSpeed");
76
+ const ulSpan = document.getElementById("ulSpeed");
77
+ const pingSpan = document.getElementById("ping");
78
 
79
+ // Utility to format bits/sec β†’ Mbps
80
+ function toMbps(bits) {
81
+ return (bits / 1e6).toFixed(2) + " Mbps";
82
+ }
83
 
84
+ startBtn.addEventListener("click", async () => {
85
+ startBtn.disabled = true;
86
+ resultsDiv.style.display = "block";
87
+ dlSpan.textContent = "Testing...";
88
+ ulSpan.textContent = "Testing...";
89
+ pingSpan.textContent = "Testing...";
90
+
91
+ // 1) Measure ping by sending a tiny GET
92
+ let pingTimes = [];
93
+ for (let i = 0; i < 5; i++) {
94
+ const t0 = performance.now();
95
+ await fetch("/ping_test?t=" + i, { cache: "no-store" });
96
+ const t1 = performance.now();
97
+ pingTimes.push(t1 - t0);
98
+ }
99
+ const avgPing = pingTimes.reduce((a, b) => a + b, 0) / pingTimes.length;
100
+ pingSpan.textContent = avgPing.toFixed(2) + " ms";
101
+
102
+ // 2) Download test: fetch the 10 MiB blob, measure duration
103
+ const dlUrl = "/download_test";
104
+ const dlStart = performance.now();
105
+ const response = await fetch(dlUrl, { cache: "no-store" });
106
+ const blob = await response.blob();
107
+ const dlEnd = performance.now();
108
+ const dlBytes = blob.size;
109
+ const dlBits = dlBytes * 8;
110
+ const dlDurationSec = (dlEnd - dlStart) / 1000;
111
+ const dlSpeedBps = dlBits / dlDurationSec; // bits/sec
112
+ dlSpan.textContent = toMbps(dlSpeedBps);
113
+
114
+ // 3) Upload test: send a random ArrayBuffer of same size (10 MiB) to /upload_test
115
+ // We reuse the DownloadSize for symmetry. In real tests, you might want a smaller upload blob.
116
+ const uploadSizeBytes = dlBytes;
117
+ // Generate random bytes
118
+ const randomBuffer = new Uint8Array(uploadSizeBytes);
119
+ window.crypto.getRandomValues(randomBuffer);
120
+
121
+ const ulStart = performance.now();
122
+ await fetch("/upload_test", {
123
+ method: "POST",
124
+ headers: { "Content-Type": "application/octet-stream" },
125
+ body: randomBuffer
126
+ });
127
+ const ulEnd = performance.now();
128
+ const ulDurationSec = (ulEnd - ulStart) / 1000;
129
+ const ulBits = uploadSizeBytes * 8;
130
+ const ulSpeedBps = ulBits / ulDurationSec;
131
+ ulSpan.textContent = toMbps(ulSpeedBps);
132
+
133
+ startBtn.disabled = false;
134
+ });
135
+ </script>
136
+ </body>
137
+ </html>
138
+ """
139
+
140
+ # Expose the HTML through Gradio’s Interface
141
+ def serve_html():
142
+ return LIBRESPEED_HTML
143
 
 
144
  iface = gr.Interface(
145
+ fn=serve_html,
146
  inputs=[],
147
+ outputs=gr.HTML(label="Speed Test"),
148
+ title="Client-Side Speed Test",
149
+ description="Measures your actual Internet link (download/upload/ping) via JavaScript."
 
 
 
 
150
  )
151
 
152
+ def gradio_thread():
153
+ """
154
+ Run Gradio in a thread so that Flask and Gradio both serve on the same port.
155
+ """
156
+ iface.launch(server_name="0.0.0.0", server_port=7860, share=False)
157
+
158
  if __name__ == "__main__":
159
+ # 1) Start Gradio in a background thread
160
+ t = threading.Thread(target=gradio_thread, daemon=True)
161
+ t.start()
162
+
163
+ # 2) Start Flask to serve endpoints (on port 7860 as well)
164
+ # Since Gradio’s Flask is already bound, we just add our routes to the same server.
165
+ # Gradio uses Flask under the hood, so these routes will be served automatically.
166
+ # (In recent Gradio versions, custom @app routes work if we set `allow_flagging="never"`, etc.)
167
+ app.run(host="0.0.0.0", port=7860, debug=False)