Spaces:
Sleeping
Sleeping
abdulsalam2121 commited on
Commit ·
99c2f3b
1
Parent(s): 534993f
Add live browser screenshot panel for dashboard
Browse files- app/browser_session.py +15 -0
- app/static/app.js +12 -0
- app/templates/index.html +6 -0
- app/web_app.py +13 -0
app/browser_session.py
CHANGED
|
@@ -85,6 +85,11 @@ class BrowserSession:
|
|
| 85 |
except Exception:
|
| 86 |
pass
|
| 87 |
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
return True
|
| 89 |
except Exception as e:
|
| 90 |
logger.error(f"Navigation failed [{url}]: {e}")
|
|
@@ -98,6 +103,16 @@ class BrowserSession:
|
|
| 98 |
try:
|
| 99 |
path = f"logs/{name}.png"
|
| 100 |
self.page.screenshot(path=path, full_page=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
logger.debug(f"Screenshot: {path}")
|
| 102 |
except Exception:
|
| 103 |
pass
|
|
|
|
| 85 |
except Exception:
|
| 86 |
pass
|
| 87 |
return False
|
| 88 |
+
try:
|
| 89 |
+
# Update the latest screenshot after successful navigation for debugging/UI
|
| 90 |
+
self.screenshot("latest")
|
| 91 |
+
except Exception:
|
| 92 |
+
pass
|
| 93 |
return True
|
| 94 |
except Exception as e:
|
| 95 |
logger.error(f"Navigation failed [{url}]: {e}")
|
|
|
|
| 103 |
try:
|
| 104 |
path = f"logs/{name}.png"
|
| 105 |
self.page.screenshot(path=path, full_page=True)
|
| 106 |
+
# Also write a consistent latest screenshot for the UI
|
| 107 |
+
try:
|
| 108 |
+
latest = Path("logs") / "screenshot_latest.png"
|
| 109 |
+
Path(path).resolve()
|
| 110 |
+
# overwrite latest
|
| 111 |
+
from shutil import copyfile
|
| 112 |
+
|
| 113 |
+
copyfile(path, str(latest))
|
| 114 |
+
except Exception:
|
| 115 |
+
pass
|
| 116 |
logger.debug(f"Screenshot: {path}")
|
| 117 |
except Exception:
|
| 118 |
pass
|
app/static/app.js
CHANGED
|
@@ -24,6 +24,7 @@ const elements = {
|
|
| 24 |
let logCursor = 0;
|
| 25 |
let pollTimer = null;
|
| 26 |
let running = false;
|
|
|
|
| 27 |
|
| 28 |
function setBanner(element, message) {
|
| 29 |
if (!message) {
|
|
@@ -204,6 +205,17 @@ function boot() {
|
|
| 204 |
setRunningState(false);
|
| 205 |
fetchStatus();
|
| 206 |
pollTimer = window.setInterval(fetchStatus, 2000);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 207 |
}
|
| 208 |
|
| 209 |
document.addEventListener('DOMContentLoaded', boot);
|
|
|
|
| 24 |
let logCursor = 0;
|
| 25 |
let pollTimer = null;
|
| 26 |
let running = false;
|
| 27 |
+
let screenshotTimer = null;
|
| 28 |
|
| 29 |
function setBanner(element, message) {
|
| 30 |
if (!message) {
|
|
|
|
| 205 |
setRunningState(false);
|
| 206 |
fetchStatus();
|
| 207 |
pollTimer = window.setInterval(fetchStatus, 2000);
|
| 208 |
+
// Start screenshot refresher
|
| 209 |
+
const img = document.getElementById('screenshotImg');
|
| 210 |
+
if (img) {
|
| 211 |
+
const refresh = () => {
|
| 212 |
+
img.src = '/screenshot?ts=' + Date.now();
|
| 213 |
+
};
|
| 214 |
+
// refresh every 3 seconds
|
| 215 |
+
screenshotTimer = window.setInterval(refresh, 3000);
|
| 216 |
+
// try an initial load
|
| 217 |
+
refresh();
|
| 218 |
+
}
|
| 219 |
}
|
| 220 |
|
| 221 |
document.addEventListener('DOMContentLoaded', boot);
|
app/templates/index.html
CHANGED
|
@@ -102,6 +102,12 @@
|
|
| 102 |
</div>
|
| 103 |
<pre id="logOutput" class="log-output">Waiting for the next run...</pre>
|
| 104 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
</section>
|
| 106 |
</main>
|
| 107 |
</div>
|
|
|
|
| 102 |
</div>
|
| 103 |
<pre id="logOutput" class="log-output">Waiting for the next run...</pre>
|
| 104 |
</div>
|
| 105 |
+
<div class="screenshot-panel">
|
| 106 |
+
<h3>Live Browser View</h3>
|
| 107 |
+
<div class="screenshot-wrap">
|
| 108 |
+
<img id="screenshotImg" src="/screenshot" alt="Latest screenshot" style="width:100%;border-radius:6px;display:block;" />
|
| 109 |
+
</div>
|
| 110 |
+
</div>
|
| 111 |
</section>
|
| 112 |
</main>
|
| 113 |
</div>
|
app/web_app.py
CHANGED
|
@@ -93,6 +93,19 @@ def status():
|
|
| 93 |
return jsonify(controller.snapshot())
|
| 94 |
|
| 95 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
@app.get("/download")
|
| 97 |
def download():
|
| 98 |
snapshot = controller.snapshot()
|
|
|
|
| 93 |
return jsonify(controller.snapshot())
|
| 94 |
|
| 95 |
|
| 96 |
+
@app.get("/screenshot")
|
| 97 |
+
def screenshot_route():
|
| 98 |
+
latest = APP_DIR / ".." / "logs" / "screenshot_latest.png"
|
| 99 |
+
# normalize
|
| 100 |
+
latest = latest.resolve()
|
| 101 |
+
if not latest.exists():
|
| 102 |
+
return ("", 204)
|
| 103 |
+
try:
|
| 104 |
+
return send_file(str(latest), mimetype="image/png")
|
| 105 |
+
except Exception:
|
| 106 |
+
return ("", 500)
|
| 107 |
+
|
| 108 |
+
|
| 109 |
@app.get("/download")
|
| 110 |
def download():
|
| 111 |
snapshot = controller.snapshot()
|