Files
mitus/cht/stream/tracker.py
2026-04-01 19:23:17 -03:00

84 lines
2.6 KiB
Python

"""
RecordingTracker: monitors the growing recording file and estimates duration.
Polls file size periodically. Uses ffprobe occasionally for accurate
duration calibration. Feeds duration updates to the Timeline.
"""
import json
import logging
import subprocess
import time
from pathlib import Path
from threading import Thread
import ffmpeg as ffmpeg_lib
log = logging.getLogger(__name__)
class RecordingTracker:
"""Tracks a growing recording file and estimates its duration."""
def __init__(self, recording_path, on_duration_update=None):
self._path = recording_path
self._on_duration = on_duration_update
self._duration = 0.0
self._avg_bitrate = None # bytes per second, calibrated by ffprobe
self._stop = False
self._thread = None
@property
def duration(self):
return self._duration
def start(self):
self._stop = False
self._thread = Thread(target=self._poll_loop, daemon=True, name="rec_tracker")
self._thread.start()
log.info("RecordingTracker started: %s", self._path)
def stop(self):
self._stop = True
log.info("RecordingTracker stopped")
def _poll_loop(self):
probe_interval = 0 # probe on first data
cycles = 0
while not self._stop:
time.sleep(2)
if not self._path.exists():
continue
size = self._path.stat().st_size
if size < 10_000:
continue
# Calibrate with ffprobe every ~30s or on first data
cycles += 1
if self._avg_bitrate is None or cycles % 15 == 0:
probed = self._probe_duration()
if probed and probed > 0 and size > 0:
self._avg_bitrate = size / probed
self._duration = probed
log.info("Probed duration: %.1fs (bitrate: %.0f B/s)",
probed, self._avg_bitrate)
elif self._avg_bitrate:
# Estimate from file size between probes
self._duration = size / self._avg_bitrate
if self._on_duration and self._duration > 0:
self._on_duration(self._duration)
def _probe_duration(self):
"""Use ffprobe to get accurate duration of the recording."""
try:
info = ffmpeg_lib.probe(str(self._path))
duration = float(info.get("format", {}).get("duration", 0))
return duration
except Exception as e:
log.debug("ffprobe failed (file still growing): %s", e)
return None