Live Counter get total count per batch

This commit is contained in:
proitlab committed 2026-07-29 14:27:49 +07:00
1 parent fe220504fd
commit e3ae89de3d
4 files changed
+82 -1

No files matched your search

+1
View File
@@ -98,6 +98,7 @@ api: GET /api/live-video → MJPEG stream from shared-memory JPEG
api: GET / → recounting HTML api: GET / → recounting HTML
api: GET /api/live-progress → proxy to LIVE_API_URL:/api/current-batch api: GET /api/live-progress → proxy to LIVE_API_URL:/api/current-batch
api: GET /api/recount-progress → proxy to RECOUNT_API_URL:/api/current-batch api: GET /api/recount-progress → proxy to RECOUNT_API_URL:/api/current-batch
api: GET /api/batch-result?file=<path> → parse `batch_NN_YYYYMMDD_HHmmSS.mp4`, query live API for count
api: GET /api/mp4-files → [ {name, path, size, mtime} ... ] api: GET /api/mp4-files → [ {name, path, size, mtime} ... ]
api: POST /api/start-recount {path} → configure go2rtc stream, return {stream_url} api: POST /api/start-recount {path} → configure go2rtc stream, return {stream_url}
api: POST /api/stop-recount → tear down go2rtc stream api: POST /api/stop-recount → tear down go2rtc stream
+39
View File
@@ -5,6 +5,7 @@ lists OUTPUT_DIR MP4 files, streams via go2rtc for preview.
""" """
import json import json
import os import os
import re
import subprocess import subprocess
import requests import requests
from datetime import datetime from datetime import datetime
@@ -34,6 +35,18 @@ FLASK_DEBUG = os.getenv("FLASK_DEBUG", "false").lower() == "true"
_http_session = requests.Session() _http_session = requests.Session()
_http_session.timeout = 3 _http_session.timeout = 3
_FILENAME_RE = re.compile(r"batch_(\d+)_(\d{8})_\d{6}\.mp4")
def _parse_filename(filename):
m = _FILENAME_RE.match(filename)
if m:
batch_num = int(m.group(1))
date_str = m.group(2)
date_formatted = f"{date_str[:4]}-{date_str[4:6]}-{date_str[6:8]}"
return batch_num, date_formatted
return None, None
def _api_get(base_url, path, default=None): def _api_get(base_url, path, default=None):
try: try:
@@ -107,6 +120,32 @@ def start_recount():
return jsonify({"success": True, "stream_url": stream_url, "file": Path(mp4_path).name}) return jsonify({"success": True, "stream_url": stream_url, "file": Path(mp4_path).name})
@app.route("/api/batch-result")
def api_batch_result():
fpath = request.args.get("file", "")
filename = Path(fpath).name
batch_num, date_str = _parse_filename(filename)
if batch_num is None:
return jsonify({"success": False, "error": "Cannot parse batch number from filename"}), 200
data = _api_get(LIVE_API_URL, f"/api/day-detail/{date_str}")
if not data:
return jsonify({"success": False, "error": "Live API unreachable"}), 200
for b in data.get("batches", []):
if b.get("batch_number") == batch_num:
return jsonify({
"success": True,
"batch_number": batch_num,
"date": date_str,
"count": b["count"],
"start_time": b.get("start_time"),
"end_time": b.get("end_time"),
})
return jsonify({"success": False, "error": f"Batch #{batch_num} not found on {date_str}"}), 200
@app.route("/api/stop-recount", methods=["POST"]) @app.route("/api/stop-recount", methods=["POST"])
def stop_recount(): def stop_recount():
try: try:
+1
View File
@@ -4,3 +4,4 @@ opencv-python
flask flask
python-dotenv python-dotenv
openpyxl openpyxl
requests
+41 -1
View File
@@ -356,6 +356,22 @@
<div class="counter-status disconnected" id="live-status"> <div class="counter-status disconnected" id="live-status">
waiting... waiting...
</div> </div>
<div class="batch-result" id="batch-result" style="display:none;margin-top:16px;padding:12px;border-top:1px solid var(--divider);">
<div style="font-size:9px;color:var(--text-secondary);letter-spacing:1px;text-transform:uppercase;margin-bottom:6px;">
Selected Batch Result
</div>
<div style="display:flex;align-items:center;justify-content:center;gap:32px;">
<div>
<div style="font-size:8px;color:var(--text-secondary);letter-spacing:1px;">BATCH</div>
<div style="font-family:'Orbitron',sans-serif;font-size:20px;font-weight:700;color:var(--accent);" id="br-batch">--</div>
</div>
<div>
<div style="font-size:8px;color:var(--text-secondary);letter-spacing:1px;">COUNT</div>
<div style="font-family:'Orbitron',sans-serif;font-size:20px;font-weight:700;color:var(--accent4);" id="br-count">--</div>
</div>
</div>
<div style="font-size:9px;color:var(--text-secondary);margin-top:6px;" id="br-date">--</div>
</div>
</div> </div>
</div> </div>
<div class="panel"> <div class="panel">
@@ -467,7 +483,31 @@ function selectFile(path, name) {
selectedFile = path; selectedFile = path;
document.getElementById('btn-start').disabled = false; document.getElementById('btn-start').disabled = false;
document.getElementById('stream-file-name').textContent = name; document.getElementById('stream-file-name').textContent = name;
loadFiles(); // refresh selection loadFiles();
loadBatchResult(path);
}
async function loadBatchResult(filePath) {
const el = document.getElementById('batch-result');
if (!filePath) {
el.style.display = 'none';
return;
}
try {
const res = await fetch('/api/batch-result?file=' + encodeURIComponent(filePath));
const data = await res.json();
if (!data.success) {
el.style.display = 'none';
return;
}
document.getElementById('br-batch').textContent = '#' + data.batch_number;
document.getElementById('br-count').textContent = data.count.toLocaleString();
document.getElementById('br-date').textContent = data.date;
el.style.display = 'block';
} catch (err) {
console.error('Failed to load batch result:', err);
el.style.display = 'none';
}
} }
function formatSize(bytes) { function formatSize(bytes) {