Live Counter get total count per batch
This commit is contained in:
1 parent
fe220504fd
commit
e3ae89de3d
4 files changed
+82
-1
No files matched your search
@@ -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
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -4,3 +4,4 @@ opencv-python
|
|||||||
flask
|
flask
|
||||||
python-dotenv
|
python-dotenv
|
||||||
openpyxl
|
openpyxl
|
||||||
|
requests
|
||||||
@@ -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) {
|
||||||
|
|||||||
Reference in new issue
Block a user