mirror of
				https://github.com/blakeblackshear/frigate.git
				synced 2025-10-27 10:52:11 +01:00 
			
		
		
		
	Save previews when restarting (#10261)
This commit is contained in:
		
							parent
							
								
									a174d0000f
								
							
						
					
					
						commit
						7be2923d2d
					
				@ -2,6 +2,8 @@
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
import multiprocessing as mp
 | 
			
		||||
import os
 | 
			
		||||
import shutil
 | 
			
		||||
import signal
 | 
			
		||||
import threading
 | 
			
		||||
from typing import Optional
 | 
			
		||||
@ -18,6 +20,7 @@ from ws4py.server.wsgiutils import WebSocketWSGIApplication
 | 
			
		||||
from frigate.comms.detections_updater import DetectionSubscriber, DetectionTypeEnum
 | 
			
		||||
from frigate.comms.ws import WebSocket
 | 
			
		||||
from frigate.config import FrigateConfig
 | 
			
		||||
from frigate.const import CACHE_DIR, CLIPS_DIR
 | 
			
		||||
from frigate.output.birdseye import Birdseye
 | 
			
		||||
from frigate.output.camera import JsmpegCamera
 | 
			
		||||
from frigate.output.preview import PreviewRecorder
 | 
			
		||||
@ -61,6 +64,8 @@ def output_frames(
 | 
			
		||||
    birdseye: Optional[Birdseye] = None
 | 
			
		||||
    preview_recorders: dict[str, PreviewRecorder] = {}
 | 
			
		||||
 | 
			
		||||
    move_preview_frames("cache")
 | 
			
		||||
 | 
			
		||||
    for camera, cam_config in config.cameras.items():
 | 
			
		||||
        if not cam_config.enabled:
 | 
			
		||||
            continue
 | 
			
		||||
@ -151,6 +156,8 @@ def output_frames(
 | 
			
		||||
    for preview in preview_recorders.values():
 | 
			
		||||
        preview.stop()
 | 
			
		||||
 | 
			
		||||
    move_preview_frames("clips")
 | 
			
		||||
 | 
			
		||||
    if birdseye is not None:
 | 
			
		||||
        birdseye.stop()
 | 
			
		||||
 | 
			
		||||
@ -160,3 +167,16 @@ def output_frames(
 | 
			
		||||
    websocket_server.shutdown()
 | 
			
		||||
    websocket_thread.join()
 | 
			
		||||
    logger.info("exiting output process...")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def move_preview_frames(loc: str):
 | 
			
		||||
    preview_holdover = os.path.join(CLIPS_DIR, "preview_restart_cache")
 | 
			
		||||
    preview_cache = os.path.join(CACHE_DIR, "preview_frames")
 | 
			
		||||
 | 
			
		||||
    if loc == "clips":
 | 
			
		||||
        shutil.move(preview_cache, preview_holdover)
 | 
			
		||||
    elif loc == "cache":
 | 
			
		||||
        if not os.path.exists(preview_holdover):
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        shutil.move(preview_holdover, preview_cache)
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,6 @@
 | 
			
		||||
import datetime
 | 
			
		||||
import logging
 | 
			
		||||
import os
 | 
			
		||||
import shutil
 | 
			
		||||
import subprocess as sp
 | 
			
		||||
import threading
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
@ -26,6 +25,7 @@ from frigate.util.image import copy_yuv_to_position, get_yuv_crop
 | 
			
		||||
logger = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
FOLDER_PREVIEW_FRAMES = "preview_frames"
 | 
			
		||||
PREVIEW_CACHE_DIR = os.path.join(CACHE_DIR, FOLDER_PREVIEW_FRAMES)
 | 
			
		||||
PREVIEW_SEGMENT_DURATION = 3600  # one hour
 | 
			
		||||
# important to have lower keyframe to maintain scrubbing performance
 | 
			
		||||
PREVIEW_KEYFRAME_INTERVAL = 60
 | 
			
		||||
@ -163,11 +163,37 @@ class PreviewRecorder:
 | 
			
		||||
            .timestamp()
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        Path(os.path.join(CACHE_DIR, "preview_frames")).mkdir(exist_ok=True)
 | 
			
		||||
        Path(PREVIEW_CACHE_DIR).mkdir(exist_ok=True)
 | 
			
		||||
        Path(os.path.join(CLIPS_DIR, f"previews/{config.name}")).mkdir(
 | 
			
		||||
            parents=True, exist_ok=True
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # check for existing items in cache
 | 
			
		||||
        start_ts = (
 | 
			
		||||
            datetime.datetime.now()
 | 
			
		||||
            .replace(minute=0, second=0, microsecond=0)
 | 
			
		||||
            .timestamp()
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        file_start = f"preview_{config.name}"
 | 
			
		||||
        start_file = f"{file_start}-{start_ts}.jpg"
 | 
			
		||||
 | 
			
		||||
        for file in sorted(os.listdir(os.path.join(CACHE_DIR, FOLDER_PREVIEW_FRAMES))):
 | 
			
		||||
            if not file.startswith(file_start):
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            if file < start_file:
 | 
			
		||||
                os.unlink(os.path.join(PREVIEW_CACHE_DIR, file))
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            ts = float(file.split("-")[1][:-4])
 | 
			
		||||
 | 
			
		||||
            if self.start_time == 0:
 | 
			
		||||
                self.start_time = ts
 | 
			
		||||
 | 
			
		||||
            self.last_output_time = ts
 | 
			
		||||
            self.output_frames.append(ts)
 | 
			
		||||
 | 
			
		||||
    def should_write_frame(
 | 
			
		||||
        self,
 | 
			
		||||
        current_tracked_objects: list[dict[str, any]],
 | 
			
		||||
@ -269,11 +295,6 @@ class PreviewRecorder:
 | 
			
		||||
            self.write_frame_to_cache(frame_time, frame)
 | 
			
		||||
 | 
			
		||||
    def stop(self) -> None:
 | 
			
		||||
        try:
 | 
			
		||||
            shutil.rmtree(os.path.join(CACHE_DIR, FOLDER_PREVIEW_FRAMES))
 | 
			
		||||
        except FileNotFoundError:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        self.requestor.stop()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user