blakeblackshear.frigate/frigate/record.py

133 lines
3.9 KiB
Python
Raw Normal View History

2020-11-30 04:31:02 +01:00
import datetime
import json
import logging
import os
import queue
import subprocess as sp
import threading
import time
from collections import defaultdict
from pathlib import Path
import psutil
from frigate.config import FrigateConfig
2020-12-01 14:22:23 +01:00
from frigate.const import RECORD_DIR, CLIPS_DIR, CACHE_DIR
2020-11-30 04:31:02 +01:00
logger = logging.getLogger(__name__)
SECONDS_IN_DAY = 60 * 60 * 24
2021-02-17 14:23:32 +01:00
2020-12-01 04:08:47 +01:00
def remove_empty_directories(directory):
2021-02-17 14:23:32 +01:00
# list all directories recursively and sort them by path,
# longest first
paths = sorted(
[x[0] for x in os.walk(RECORD_DIR)],
key=lambda p: len(str(p)),
reverse=True,
)
for path in paths:
# don't delete the parent
if path == RECORD_DIR:
continue
if len(os.listdir(path)) == 0:
os.rmdir(path)
2020-12-01 04:08:47 +01:00
2020-11-30 04:31:02 +01:00
class RecordingMaintainer(threading.Thread):
def __init__(self, config: FrigateConfig, stop_event):
threading.Thread.__init__(self)
2021-02-17 14:23:32 +01:00
self.name = "recording_maint"
2020-11-30 04:31:02 +01:00
self.config = config
self.stop_event = stop_event
def move_files(self):
2021-02-17 14:23:32 +01:00
recordings = [
d
for d in os.listdir(RECORD_DIR)
if os.path.isfile(os.path.join(RECORD_DIR, d)) and d.endswith(".mp4")
]
2020-11-30 04:31:02 +01:00
files_in_use = []
for process in psutil.process_iter():
try:
2021-02-17 14:23:32 +01:00
if process.name() != "ffmpeg":
2020-12-24 21:23:59 +01:00
continue
2020-11-30 04:31:02 +01:00
flist = process.open_files()
if flist:
for nt in flist:
2020-12-01 14:22:23 +01:00
if nt.path.startswith(RECORD_DIR):
2021-02-17 14:23:32 +01:00
files_in_use.append(nt.path.split("/")[-1])
2020-11-30 04:31:02 +01:00
except:
continue
for f in recordings:
if f in files_in_use:
continue
basename = os.path.splitext(f)[0]
camera, date = basename.rsplit("-", maxsplit=1)
start_time = datetime.datetime.strptime(date, "%Y%m%d%H%M%S")
ffprobe_cmd = [
"ffprobe",
"-v",
"error",
"-show_entries",
"format=duration",
"-of",
"default=noprint_wrappers=1:nokey=1",
f"{os.path.join(RECORD_DIR, f)}",
]
p = sp.run(ffprobe_cmd, capture_output=True)
if p.returncode == 0:
duration = float(p.stdout.decode().strip())
2020-11-30 04:31:02 +01:00
else:
logger.info(f"bad file: {f}")
2021-02-17 14:23:32 +01:00
os.remove(os.path.join(RECORD_DIR, f))
2020-11-30 04:31:02 +01:00
continue
2021-02-17 14:23:32 +01:00
directory = os.path.join(
RECORD_DIR, start_time.strftime("%Y-%m/%d/%H"), camera
)
2020-11-30 04:31:02 +01:00
if not os.path.exists(directory):
os.makedirs(directory)
file_name = f"{start_time.strftime('%M.%S.mp4')}"
2021-02-17 14:23:32 +01:00
os.rename(os.path.join(RECORD_DIR, f), os.path.join(directory, file_name))
2020-11-30 04:31:02 +01:00
def expire_files(self):
delete_before = {}
for name, camera in self.config.cameras.items():
2021-02-17 14:23:32 +01:00
delete_before[name] = (
datetime.datetime.now().timestamp()
- SECONDS_IN_DAY * camera.record.retain_days
)
2020-11-30 04:31:02 +01:00
2021-02-17 14:23:32 +01:00
for p in Path("/media/frigate/recordings").rglob("*.mp4"):
2020-12-22 01:17:25 +01:00
if not p.parent.name in delete_before:
2020-11-30 04:31:02 +01:00
continue
2020-12-22 01:17:25 +01:00
if p.stat().st_mtime < delete_before[p.parent.name]:
2020-11-30 04:31:02 +01:00
p.unlink(missing_ok=True)
def run(self):
counter = 0
self.expire_files()
2021-02-17 14:23:32 +01:00
while True:
2020-11-30 04:31:02 +01:00
if self.stop_event.is_set():
logger.info(f"Exiting recording maintenance...")
break
# only expire events every 10 minutes, but check for new files every 10 seconds
time.sleep(10)
counter = counter + 1
2020-12-01 04:08:47 +01:00
if counter > 60:
2020-11-30 04:31:02 +01:00
self.expire_files()
2020-12-01 14:22:23 +01:00
remove_empty_directories(RECORD_DIR)
2020-12-01 04:08:47 +01:00
counter = 0
2020-11-30 04:31:02 +01:00
self.move_files()