def vod_event(id): try: event: Event = Event.get(Event.id == id) except DoesNotExist: logger.error(f"Event not found: {id}") return "Event not found.", 404 if not event.has_clip: logger.error(f"Event does not have recordings: {id}") return "Recordings not available", 404 clip_path = os.path.join(CLIPS_DIR, f"{event.camera}-{id}.mp4") if not os.path.isfile(clip_path): end_ts = (datetime.now().timestamp() if event.end_time is None else event.end_time) vod_response = vod_ts(event.camera, event.start_time, end_ts) # If the recordings are not found, set has_clip to false if (type(vod_response) == tuple and len(vod_response) == 2 and vod_response[1] == 404): Event.update(has_clip=False).where(Event.id == id).execute() return vod_response duration = int((event.end_time - event.start_time) * 1000) return jsonify({ "cache": True, "discontinuity": False, "durations": [duration], "sequences": [{ "clips": [{ "type": "source", "path": clip_path }] }], })
def expire(self, media_type): ## Expire events from unlisted cameras based on the global config if media_type == 'clips': retain_config = self.config.clips.retain file_extension = "mp4" update_params = {"has_clip": False} else: retain_config = self.config.snapshots.retain file_extension = "jpg" update_params = {"has_snapshot": False} distinct_labels = (Event.select(Event.label).where( Event.camera.not_in(self.camera_keys)).distinct()) # loop over object types in db for l in distinct_labels: # get expiration time for this label expire_days = retain_config.objects.get(l.label, retain_config.default) expire_after = (datetime.datetime.now() - datetime.timedelta(days=expire_days)).timestamp() # grab all events after specific time expired_events = Event.select().where( Event.camera.not_in(self.camera_keys), Event.start_time < expire_after, Event.label == l.label, ) # delete the media from disk for event in expired_events: media_name = f"{event.camera}-{event.id}" media_path = Path( f"{os.path.join(CLIPS_DIR, media_name)}.{file_extension}") media_path.unlink(missing_ok=True) # update the clips attribute for the db entry update_query = Event.update(update_params).where( Event.camera.not_in(self.camera_keys), Event.start_time < expire_after, Event.label == l.label, ) update_query.execute() ## Expire events from cameras based on the camera config for name, camera in self.config.cameras.items(): if media_type == 'clips': retain_config = camera.clips.retain else: retain_config = camera.snapshots.retain # get distinct objects in database for this camera distinct_labels = (Event.select( Event.label).where(Event.camera == name).distinct()) # loop over object types in db for l in distinct_labels: # get expiration time for this label expire_days = retain_config.objects.get( l.label, retain_config.default) expire_after = ( datetime.datetime.now() - datetime.timedelta(days=expire_days)).timestamp() # grab all events after specific time expired_events = Event.select().where( Event.camera == name, Event.start_time < expire_after, Event.label == l.label, ) # delete the grabbed clips from disk for event in expired_events: media_name = f"{event.camera}-{event.id}" media_path = Path( f"{os.path.join(CLIPS_DIR, media_name)}.{file_extension}" ) media_path.unlink(missing_ok=True) # update the clips attribute for the db entry update_query = Event.update(update_params).where( Event.camera == name, Event.start_time < expire_after, Event.label == l.label, ) update_query.execute()
def run(self): # set an end_time on events without an end_time on startup Event.update(end_time=Event.start_time + 30).where(Event.end_time == None).execute() while not self.stop_event.is_set(): try: event_type, camera, event_data = self.event_queue.get( timeout=10) except queue.Empty: continue logger.debug( f"Event received: {event_type} {camera} {event_data['id']}") event_config: EventsConfig = self.config.cameras[ camera].record.events if event_type == "start": self.events_in_process[event_data["id"]] = event_data elif event_type == "update" and should_update_db( self.events_in_process[event_data["id"]], event_data): self.events_in_process[event_data["id"]] = event_data # TODO: this will generate a lot of db activity possibly if event_data["has_clip"] or event_data["has_snapshot"]: Event.replace( id=event_data["id"], label=event_data["label"], camera=camera, start_time=event_data["start_time"] - event_config.pre_capture, end_time=None, top_score=event_data["top_score"], false_positive=event_data["false_positive"], zones=list(event_data["entered_zones"]), thumbnail=event_data["thumbnail"], region=event_data["region"], box=event_data["box"], area=event_data["area"], has_clip=event_data["has_clip"], has_snapshot=event_data["has_snapshot"], ).execute() elif event_type == "end": if event_data["has_clip"] or event_data["has_snapshot"]: Event.replace( id=event_data["id"], label=event_data["label"], camera=camera, start_time=event_data["start_time"] - event_config.pre_capture, end_time=event_data["end_time"] + event_config.post_capture, top_score=event_data["top_score"], false_positive=event_data["false_positive"], zones=list(event_data["entered_zones"]), thumbnail=event_data["thumbnail"], region=event_data["region"], box=event_data["box"], area=event_data["area"], has_clip=event_data["has_clip"], has_snapshot=event_data["has_snapshot"], ).execute() del self.events_in_process[event_data["id"]] self.event_processed_queue.put((event_data["id"], camera)) # set an end_time on events without an end_time before exiting Event.update(end_time=datetime.datetime.now().timestamp()).where( Event.end_time == None).execute() logger.info(f"Exiting event processor...")