Stream State across browser

This commit is contained in:
proitlab committed 2026-07-29 15:18:53 +07:00
1 parent 391fc9ecd9
commit 2d98ea2d17
2 files changed
+85 -11

No files matched your search

+26 -1
View File
@@ -42,6 +42,7 @@ _FILENAME_RE = re.compile(r"batch[_-](\d+)[_-](\d{8})[_-]\d{6}\.mp4", re.IGNOREC
_streaming_mp4 = None
_streaming_lock = threading.Lock()
_streaming_proc = None
_current_file = None
def _parse_filename(filename):
@@ -115,7 +116,7 @@ def api_mp4_files():
@app.route("/api/start-recount", methods=["POST"])
def start_recount():
global _streaming_mp4, _streaming_proc
global _streaming_mp4, _streaming_proc, _current_file
data = request.get_json(force=True) or {}
mp4_path = data.get("path", "")
if not mp4_path:
@@ -125,6 +126,7 @@ def start_recount():
with _streaming_lock:
_stop_ffmpeg_locked()
_current_file = mp4_path
cmd = [
"ffmpeg", "-re", "-i", mp4_path,
"-vf", "scale=640:480",
@@ -139,16 +141,39 @@ def start_recount():
"success": True,
"stream_url": f"{RECOUNT_API_URL}/api/live-video",
"file": Path(mp4_path).name,
"path": mp4_path,
})
@app.route("/api/stop-recount", methods=["POST"])
def stop_recount():
global _current_file
with _streaming_lock:
_stop_ffmpeg_locked()
_current_file = None
return jsonify({"success": True})
@app.route("/api/state")
def api_state():
with _streaming_lock:
streaming = _streaming_proc is not None and _streaming_proc.poll() is None
mp4 = _streaming_mp4
current_file = _current_file
result = {
"streaming": streaming,
}
if current_file:
result["file"] = current_file
result["file_name"] = Path(current_file).name
result["stream_url"] = f"{RECOUNT_API_URL}/api/live-video"
batch_num, date_str = _parse_filename(Path(current_file).name)
if batch_num is not None:
result["batch_number"] = batch_num
result["batch_date"] = date_str
return jsonify(result)
@app.route("/api/download-mp4")
def api_download_mp4():
fpath = request.args.get("file", "")
+59 -10
View File
@@ -434,11 +434,56 @@ let streamUrl = '';
document.addEventListener('DOMContentLoaded', () => {
loadRecountProgress();
loadFiles();
syncState();
setInterval(loadRecountProgress, 500);
setInterval(loadFiles, 30000);
setInterval(syncState, 1000);
});
async function syncState() {
try {
const res = await fetch('/api/state');
const data = await res.json();
applyState(data);
} catch (err) {}
}
function applyState(data) {
if (!data) return;
if (data.file) {
selectedFile = data.file;
document.getElementById('btn-start').disabled = data.streaming;
document.getElementById('btn-download').disabled = false;
}
if (data.streaming && data.file) {
streaming = true;
streamUrl = data.stream_url;
const img = document.getElementById('stream-img');
const placeholder = document.getElementById('video-placeholder');
if (img.src.indexOf(streamUrl) === -1) {
img.src = streamUrl + '?' + Date.now();
img.style.display = 'block';
if (placeholder) placeholder.style.display = 'none';
}
document.getElementById('btn-stop').disabled = false;
document.getElementById('btn-start').disabled = true;
document.getElementById('btn-start').textContent = 'STREAMING';
document.getElementById('btn-start').classList.add('btn-ghost');
document.getElementById('video-status').innerHTML =
'<span style="width:6px;height:6px;border-radius:50%;background:var(--accent4);box-shadow:0 0 6px var(--accent4);animation:blink 1.5s ease-in-out infinite;"></span> streaming ' + (data.file_name || '');
document.getElementById('video-status').className = 'video-status connected';
loadBatchResult(data.file);
} else if (!data.streaming && data.file) {
loadBatchResult(data.file);
}
loadFiles();
}
// --- File list ---
async function loadFiles() {
@@ -485,8 +530,8 @@ function selectFile(path, name) {
return;
}
selectedFile = path;
document.getElementById('btn-start').disabled = false;
document.getElementById('btn-download').disabled = false;
if (!streaming) document.getElementById('btn-start').disabled = false;
loadFiles();
loadBatchResult(path);
}
@@ -557,7 +602,6 @@ function downloadFile() {
async function startRecount() {
if (!selectedFile) return;
const btnStart = document.getElementById('btn-start');
const btnStop = document.getElementById('btn-stop');
btnStart.disabled = true;
btnStart.textContent = 'CONNECTING...';
@@ -576,6 +620,7 @@ async function startRecount() {
}
streamUrl = data.stream_url;
streaming = true;
selectedFile = data.path;
const img = document.getElementById('stream-img');
const placeholder = document.getElementById('video-placeholder');
@@ -583,15 +628,9 @@ async function startRecount() {
img.style.display = 'block';
if (placeholder) placeholder.style.display = 'none';
btnStop.disabled = false;
document.getElementById('btn-stop').disabled = false;
btnStart.textContent = 'STREAMING';
btnStart.classList.add('btn-ghost');
document.getElementById('video-status').innerHTML =
'<span style="width:6px;height:6px;border-radius:50%;background:var(--accent4);box-shadow:0 0 6px var(--accent4);animation:blink 1.5s ease-in-out infinite;"></span> streaming ' + data.file;
document.getElementById('video-status').className = 'video-status connected';
loadFiles();
} catch (err) {
alert('Error: ' + err.message);
btnStart.disabled = false;
@@ -605,6 +644,8 @@ async function stopRecount() {
} catch (err) {}
streaming = false;
streamUrl = '';
selectedFile = null;
batchResultLoaded = false;
const img = document.getElementById('stream-img');
const placeholder = document.getElementById('video-placeholder');
@@ -614,7 +655,7 @@ async function stopRecount() {
const btnStart = document.getElementById('btn-start');
const btnStop = document.getElementById('btn-stop');
btnStart.disabled = selectedFile ? false : true;
btnStart.disabled = true;
btnStart.textContent = '▶ START RECOUNT';
btnStart.classList.remove('btn-ghost');
btnStop.disabled = true;
@@ -623,6 +664,14 @@ async function stopRecount() {
'<span style="width:6px;height:6px;border-radius:50%;background:var(--text-secondary);"></span> stopped';
document.getElementById('video-status').className = 'video-status stopped';
document.getElementById('live-title').innerHTML = '&#9673; Live Counter';
document.getElementById('live-count').textContent = '--';
document.getElementById('live-label').textContent = 'Objects Counted';
document.getElementById('live-batch').textContent = '--';
document.getElementById('live-time').textContent = '--';
document.getElementById('live-status').textContent = 'no file selected';
document.getElementById('live-status').className = 'counter-status disconnected';
loadFiles();
}