A capture crash can leave events with no frames, no Length and no
EndDateTime. The API's EndTimeSecs virtual field reported these with an
end of NOW(), the fallback meant for a genuinely in-progress recording,
so every orphan appeared to span from its start until now.
On a monitor with hundreds of these, the events all overlapped: at a
typical timestamp 313 events covered the same instant. findEventByTime is
a binary search that assumes non-overlapping ordered events, so it
returned an essentially arbitrary one, and zms then streamed a different
event than the JS believed was playing. That is the erratic playback and
event/stream mismatch seen in montage review.
Two changes:
- Event model EndTimeSecs: for an event with no EndDateTime and Length 0,
report end = StartDateTime (zero duration) instead of NOW(). An event
with no length has no span to occupy, so it no longer overlaps others.
- montagereview receive_events: skip events with no frames. There is
nothing to review in an empty event, and dropping them keeps the
timeline and event selection to real recordings.
The same NOW() fallback exists in web/ajax/events.php and
web/skins/classic/views/montagereview.php; those paths are not used by
montage review's event load and are left unchanged.
Verified on a monitor with 521 orphaned events: overlapping events at a
given instant dropped from 313 to 2, and the streamed event now matches
the selected event with sub-second drift.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When an event closes the recording file is renamed from incomplete.* to
<Id>-video.* and the DB row is updated. A stale Event model still reports
DefaultVideo=incomplete.mp4, producing spurious 'File does not exist'
warnings. When the incomplete file is missing, clear the object cache and
re-read the event from the database to check the finished file before
warning.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When zmc is killed or crashes without writing EndDateTime, three code
paths invent a fake end of NOW(), so an event from hours ago appears to
extend across the entire down-time. Montage review then paints a bar
that makes it look like recorded video exists where it doesn't.
Length is flushed to the DB every few seconds during recording, so even
crashed events have an accurate last-known duration. Fall back to
StartDateTime + Length when EndDateTime IS NULL, and only fall back to
NOW() when Length is also 0 (event has no recorded data yet).
- web/api/app/Model/Event.php: EndTimeSecs and EndTime virtual fields,
which is what the montagereview JS actually reads via the API.
- web/ajax/events.php: same fix in the AJAX events list SQL.
- web/skins/classic/views/montagereview.php: \$eventsSql kept in sync
even though it is no longer executed directly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* include includes/functions.php so that we have access to all it's contents
* add a beforeDelete function which deletes the files. Add other needed functions like Path() LinkPath() etc.
* add require_once for Storage and functions because we use them in Event
* Now that ZM has namespaces use the ZM Event class to do the heavy lifting of delete
* Don't need functions in AppController anymore