From 180b3a4c421f4ccb20845ca2f2bee44dea5a2f1b Mon Sep 17 00:00:00 2001 From: Michael Lopez Date: Tue, 15 Oct 2024 12:41:11 +0200 Subject: [PATCH 01/11] #16 Supporting Last image as idle stream if exists --- .gitignore | 1 + camera.py | 44 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 7384a17..5cdf7c9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ __pycache__ venv .env +.DS_Store diff --git a/camera.py b/camera.py index 68ec026..9626bce 100644 --- a/camera.py +++ b/camera.py @@ -5,6 +5,7 @@ import os from device import Device from decouple import config +from pyaarlo.util import http_get DEBUG = config('DEBUG', default=False, cast=bool) @@ -154,12 +155,45 @@ async def _start_idle_stream(self): Start idle picture, writing to the proxy stream """ exit_code = 1 + idle_stream_command = ['ffmpeg', '-re', '-stream_loop', '-1', '-i', 'idle.mp4', + '-c:v', 'copy', + '-c:a', 'libmp3lame', '-ar', '44100', '-b:a', '8k', + '-bsf', 'dump_extra', '-f', 'mpegts', 'pipe:'] + + image_path = "/tmp/{}.jpg".format(self.name) + last_image = http_get(self._arlo.last_image, filename=image_path) + + # Using last camera's thumbnail as idle stream if exists + if last_image: + # Converting last_image to a video for loop_stream (less CPU consumsion) + convert = await asyncio.create_subprocess_exec( + *['ffmpeg', + '-framerate', '1', + '-i', image_path, + '-c:v', 'libx264', '-r', '30', '-vf', 'scale=640:-2', "/tmp/{}.mp4".format(self.name) + ], + stdin=subprocess.DEVNULL, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE if DEBUG else subprocess.DEVNULL + ) + + if DEBUG: + asyncio.create_task( + self._log_stderr(convert, 'idle_stream') + ) + + convert_exit_code = await convert.wait() + if convert_exit_code == 0: + idle_stream_command = ['ffmpeg', + '-re', '-stream_loop', '-1', + '-i', "/tmp/{}.mp4".format(self.name), + '-c:v', 'copy', '-shortest', + '-f', 'mpegts', 'pipe:' + ] + while exit_code > 0: self.stream = await asyncio.create_subprocess_exec( - *['ffmpeg', '-re', '-stream_loop', '-1', '-i', 'idle.mp4', - '-c:v', 'copy', - '-c:a', 'libmp3lame', '-ar', '44100', '-b:a', '8k', - '-bsf', 'dump_extra', '-f', 'mpegts', 'pipe:'], + *idle_stream_command, stdin=subprocess.DEVNULL, stdout=self.proxy_writer, stderr=subprocess.PIPE if DEBUG else subprocess.DEVNULL @@ -202,6 +236,8 @@ async def _start_stream(self): asyncio.create_task( self._log_stderr(self.stream, 'live_stream') ) + else: + logging.debug(f"{self.name}: No stream available.") async def _stream_timeout(self): await asyncio.sleep(self.timeout) From dad364b5f386d161239b2c822d9ede47a7793fa3 Mon Sep 17 00:00:00 2001 From: kaffetorsk Date: Tue, 15 Oct 2024 12:41:11 +0200 Subject: [PATCH 02/11] overwrite last video --- camera.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/camera.py b/camera.py index 9626bce..703b622 100644 --- a/camera.py +++ b/camera.py @@ -167,7 +167,7 @@ async def _start_idle_stream(self): if last_image: # Converting last_image to a video for loop_stream (less CPU consumsion) convert = await asyncio.create_subprocess_exec( - *['ffmpeg', + *['ffmpeg', '-y', '-framerate', '1', '-i', image_path, '-c:v', 'libx264', '-r', '30', '-vf', 'scale=640:-2', "/tmp/{}.mp4".format(self.name) From 891da27780db6a43e47629857358fc20971995fb Mon Sep 17 00:00:00 2001 From: kaffetorsk Date: Tue, 15 Oct 2024 12:41:11 +0200 Subject: [PATCH 03/11] add debuging --- camera.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/camera.py b/camera.py index 703b622..17e458c 100644 --- a/camera.py +++ b/camera.py @@ -165,6 +165,9 @@ async def _start_idle_stream(self): # Using last camera's thumbnail as idle stream if exists if last_image: + logging.debug( + f"Last image found for {self.name}, setting as idle" + ) # Converting last_image to a video for loop_stream (less CPU consumsion) convert = await asyncio.create_subprocess_exec( *['ffmpeg', '-y', From 405cd7d1d8eb69120a91de87cf125b89748cef76 Mon Sep 17 00:00:00 2001 From: kaffetorsk Date: Tue, 15 Oct 2024 12:41:11 +0200 Subject: [PATCH 04/11] code styling --- camera.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/camera.py b/camera.py index 17e458c..07037f2 100644 --- a/camera.py +++ b/camera.py @@ -155,44 +155,47 @@ async def _start_idle_stream(self): Start idle picture, writing to the proxy stream """ exit_code = 1 - idle_stream_command = ['ffmpeg', '-re', '-stream_loop', '-1', '-i', 'idle.mp4', + idle_stream_command = [ + 'ffmpeg', '-re', '-stream_loop', '-1', '-i', 'idle.mp4', '-c:v', 'copy', '-c:a', 'libmp3lame', '-ar', '44100', '-b:a', '8k', - '-bsf', 'dump_extra', '-f', 'mpegts', 'pipe:'] - + '-bsf', 'dump_extra', '-f', 'mpegts', 'pipe:' + ] + image_path = "/tmp/{}.jpg".format(self.name) last_image = http_get(self._arlo.last_image, filename=image_path) - + # Using last camera's thumbnail as idle stream if exists if last_image: logging.debug( f"Last image found for {self.name}, setting as idle" ) - # Converting last_image to a video for loop_stream (less CPU consumsion) + # Converting last_image to a video for loop_stream (less CPU usage) convert = await asyncio.create_subprocess_exec( *['ffmpeg', '-y', '-framerate', '1', '-i', image_path, - '-c:v', 'libx264', '-r', '30', '-vf', 'scale=640:-2', "/tmp/{}.mp4".format(self.name) - ], + '-c:v', 'libx264', '-r', '30', '-vf', 'scale=640:-2', + "/tmp/{}.mp4".format(self.name)], stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE if DEBUG else subprocess.DEVNULL ) - + if DEBUG: asyncio.create_task( self._log_stderr(convert, 'idle_stream') ) - + convert_exit_code = await convert.wait() if convert_exit_code == 0: - idle_stream_command = ['ffmpeg', + idle_stream_command = [ + 'ffmpeg', '-re', '-stream_loop', '-1', '-i', "/tmp/{}.mp4".format(self.name), '-c:v', 'copy', '-shortest', '-f', 'mpegts', 'pipe:' - ] + ] while exit_code > 0: self.stream = await asyncio.create_subprocess_exec( From 750b1fdfc0c23711cdf590e428c664c22538c831 Mon Sep 17 00:00:00 2001 From: kaffetorsk Date: Tue, 15 Oct 2024 12:49:22 +0200 Subject: [PATCH 05/11] Added config option --- README.md | 1 + camera.py | 66 ++++++++++++++++++++++++++++--------------------------- main.py | 3 ++- 3 files changed, 37 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 80d968c..7257f0a 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ PYAARLO_STREAM_TIMEOUT: Pyaarlo backend event stream timeout (in seconds) (defau PYAARLO_STORAGE_DIR: Pyaarlo storage_dir. Define it if you want to change the Pyaarlo storage directory. (default determined by pyaarlo) PYAARLO_ECDH_CURVE: Allowing you defining the ECDH CURVE to use during pyaarlo authentication IMAP_GRAB_ALL: Grabs all mails in the inbox, to avoid slow indexing problems (default: False) +LAST_IMAGE_IDLE: Set last frame as idle image for the camera (default: False) ``` ### Running ``` diff --git a/camera.py b/camera.py index 07037f2..f8aff1a 100644 --- a/camera.py +++ b/camera.py @@ -30,10 +30,11 @@ class Camera(Device): STATES = ['idle', 'streaming'] def __init__(self, arlo_camera, ffmpeg_out, - motion_timeout, status_interval): + motion_timeout, status_interval, last_image_idle): super().__init__(arlo_camera, status_interval) self.ffmpeg_out = shlex.split(ffmpeg_out.format(name=self.name)) self.timeout = motion_timeout + self.last_image_idle = last_image_idle self._timeout_task = None self.motion = False self._state = None @@ -162,40 +163,41 @@ async def _start_idle_stream(self): '-bsf', 'dump_extra', '-f', 'mpegts', 'pipe:' ] - image_path = "/tmp/{}.jpg".format(self.name) - last_image = http_get(self._arlo.last_image, filename=image_path) - - # Using last camera's thumbnail as idle stream if exists - if last_image: - logging.debug( - f"Last image found for {self.name}, setting as idle" - ) - # Converting last_image to a video for loop_stream (less CPU usage) - convert = await asyncio.create_subprocess_exec( - *['ffmpeg', '-y', - '-framerate', '1', - '-i', image_path, - '-c:v', 'libx264', '-r', '30', '-vf', 'scale=640:-2', - "/tmp/{}.mp4".format(self.name)], - stdin=subprocess.DEVNULL, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE if DEBUG else subprocess.DEVNULL - ) + if self.last_image_idle: + image_path = "/tmp/{}.jpg".format(self.name) + last_image = http_get(self._arlo.last_image, filename=image_path) - if DEBUG: - asyncio.create_task( - self._log_stderr(convert, 'idle_stream') + # Using last camera's thumbnail as idle stream if exists + if last_image: + logging.debug( + f"Last image found for {self.name}, setting as idle" + ) + # Converting last_image to a video for loop_stream + convert = await asyncio.create_subprocess_exec( + *['ffmpeg', '-y', + '-framerate', '1', + '-i', image_path, + '-c:v', 'libx264', '-r', '30', '-vf', 'scale=640:-2', + "/tmp/{}.mp4".format(self.name)], + stdin=subprocess.DEVNULL, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE if DEBUG else subprocess.DEVNULL ) - convert_exit_code = await convert.wait() - if convert_exit_code == 0: - idle_stream_command = [ - 'ffmpeg', - '-re', '-stream_loop', '-1', - '-i', "/tmp/{}.mp4".format(self.name), - '-c:v', 'copy', '-shortest', - '-f', 'mpegts', 'pipe:' - ] + if DEBUG: + asyncio.create_task( + self._log_stderr(convert, 'idle_stream') + ) + + convert_exit_code = await convert.wait() + if convert_exit_code == 0: + idle_stream_command = [ + 'ffmpeg', + '-re', '-stream_loop', '-1', + '-i', "/tmp/{}.mp4".format(self.name), + '-c:v', 'copy', '-shortest', + '-f', 'mpegts', 'pipe:' + ] while exit_code > 0: self.stream = await asyncio.create_subprocess_exec( diff --git a/main.py b/main.py index 1fc739d..aee0e20 100644 --- a/main.py +++ b/main.py @@ -17,6 +17,7 @@ FFMPEG_OUT = config('FFMPEG_OUT') MOTION_TIMEOUT = config('MOTION_TIMEOUT', default=60, cast=int) STATUS_INTERVAL = config('STATUS_INTERVAL', default=120, cast=int) +LAST_IMAGE_IDLE = config('LAST_IMAGE_IDLE', default=False) DEBUG = config('DEBUG', default=False, cast=bool) PYAARLO_BACKEND = config('PYAARLO_BACKEND', default=None) PYAARLO_REFRESH_DEVICES = config('PYAARLO_REFRESH_DEVICES', default=0, cast=int) @@ -68,7 +69,7 @@ async def main(): # Initialize cameras cameras = [Camera( - c, FFMPEG_OUT, MOTION_TIMEOUT, STATUS_INTERVAL + c, FFMPEG_OUT, MOTION_TIMEOUT, STATUS_INTERVAL, LAST_IMAGE_IDLE ) for c in arlo.cameras] # Start both From 345df3b4988310e39d9a0c562aefce9479a813e3 Mon Sep 17 00:00:00 2001 From: kaffetorsk Date: Mon, 21 Oct 2024 00:26:43 +0200 Subject: [PATCH 06/11] non-blocking download --- camera.py | 9 +++++---- requirements.txt | 1 + utils.py | 17 +++++++++++++++++ 3 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 utils.py diff --git a/camera.py b/camera.py index f8aff1a..3b643b8 100644 --- a/camera.py +++ b/camera.py @@ -5,7 +5,7 @@ import os from device import Device from decouple import config -from pyaarlo.util import http_get +from utils import download_file DEBUG = config('DEBUG', default=False, cast=bool) @@ -164,9 +164,10 @@ async def _start_idle_stream(self): ] if self.last_image_idle: - image_path = "/tmp/{}.jpg".format(self.name) - last_image = http_get(self._arlo.last_image, filename=image_path) - + image_path = f"/tmp/{self.name}.jpg" + last_image = await download_file( + self._arlo.last_image, image_path + ) # Using last camera's thumbnail as idle stream if exists if last_image: logging.debug( diff --git a/requirements.txt b/requirements.txt index b699ada..a4d3d5d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ git+https://github.com/kaffetorsk/pyaarlo@grab_all python-decouple aiomqtt==1.2.1 aiostream +aiohttp diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..3bad8b3 --- /dev/null +++ b/utils.py @@ -0,0 +1,17 @@ +import aiohttp + + +async def download_file(url, dest_filename): + async with aiohttp.ClientSession() as session: + async with session.get(url) as response: + if response.status == 200: + # Read file data + with open(dest_filename, 'wb') as f: + while True: + chunk = await response.content.read(1024) + if not chunk: + break + f.write(chunk) + return True + else: + return False From 52fd42dececd7b6f67f1c21dcc8e7147425966f0 Mon Sep 17 00:00:00 2001 From: kaffetorsk Date: Mon, 21 Oct 2024 00:27:15 +0200 Subject: [PATCH 07/11] refactored idle image --- Dockerfile | 2 +- camera.py | 140 ++++++++++++++++++++++++++++++++++++++++------------- device.py | 1 - main.py | 6 ++- 4 files changed, 111 insertions(+), 38 deletions(-) diff --git a/Dockerfile b/Dockerfile index c061c80..7eee3b8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,7 @@ RUN python3.11 -m ensurepip # Unbuffered logging ENV PYTHONUNBUFFERED=TRUE -COPY idle.mp4 requirements.txt ./ +COPY idle.mp4 eye.png requirements.txt ./ RUN pip3.11 install -U --upgrade-strategy eager -r requirements.txt diff --git a/camera.py b/camera.py index 3b643b8..d0f9da6 100644 --- a/camera.py +++ b/camera.py @@ -3,6 +3,7 @@ import asyncio import shlex import os +import re from device import Device from decouple import config from utils import download_file @@ -30,7 +31,8 @@ class Camera(Device): STATES = ['idle', 'streaming'] def __init__(self, arlo_camera, ffmpeg_out, - motion_timeout, status_interval, last_image_idle): + motion_timeout, status_interval, last_image_idle, + default_resolution): super().__init__(arlo_camera, status_interval) self.ffmpeg_out = shlex.split(ffmpeg_out.format(name=self.name)) self.timeout = motion_timeout @@ -44,6 +46,10 @@ def __init__(self, arlo_camera, ffmpeg_out, self.proxy_reader, self.proxy_writer = os.pipe() self._pictures = asyncio.Queue() self._listen_pictures = False + self._default_resolution = default_resolution + self.resolution = None + self.idle_video = None + self.event_loop = asyncio.get_running_loop() logging.info(f"Camera added: {self.name}") async def run(self): @@ -52,8 +58,18 @@ async def run(self): Creates event channel between pyaarlo callbacks and async generator. Listens for and passes events to handler. """ - while self._arlo.is_unavailable: + while ( + self._arlo.is_unavailable + or (self._arlo.has_batteries and self._arlo.battery_level == 0) + or not self._arlo.is_on + ): await asyncio.sleep(5) + logging.info(f"{self.name} availaible, starting stream") + + # Start stream to get resolution + await self._start_stream() + self.stop_stream() + await self.set_state('idle') asyncio.create_task(self._start_proxy_stream()) await super().run() @@ -155,14 +171,9 @@ async def _start_idle_stream(self): """ Start idle picture, writing to the proxy stream """ - exit_code = 1 - idle_stream_command = [ - 'ffmpeg', '-re', '-stream_loop', '-1', '-i', 'idle.mp4', - '-c:v', 'copy', - '-c:a', 'libmp3lame', '-ar', '44100', '-b:a', '8k', - '-bsf', 'dump_extra', '-f', 'mpegts', 'pipe:' - ] + default_image_path = "eye.png" + # Create video from last image if configured if self.last_image_idle: image_path = f"/tmp/{self.name}.jpg" last_image = await download_file( @@ -173,36 +184,26 @@ async def _start_idle_stream(self): logging.debug( f"Last image found for {self.name}, setting as idle" ) - # Converting last_image to a video for loop_stream - convert = await asyncio.create_subprocess_exec( - *['ffmpeg', '-y', - '-framerate', '1', - '-i', image_path, - '-c:v', 'libx264', '-r', '30', '-vf', 'scale=640:-2', - "/tmp/{}.mp4".format(self.name)], - stdin=subprocess.DEVNULL, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE if DEBUG else subprocess.DEVNULL - ) + self.idle_video = await self._create_idle_video(image_path) - if DEBUG: - asyncio.create_task( - self._log_stderr(convert, 'idle_stream') - ) + # Either not configured for last_image_idle, or it failed to create + # create idle video from default image to cameras resolution + if not self.idle_video: + self.idle_video = await self._create_idle_video(default_image_path) + + # Still no idle_video present, revert to default video + if not self.idle_video: + self.idle_video = "idle.mp4" - convert_exit_code = await convert.wait() - if convert_exit_code == 0: - idle_stream_command = [ - 'ffmpeg', - '-re', '-stream_loop', '-1', - '-i', "/tmp/{}.mp4".format(self.name), - '-c:v', 'copy', '-shortest', - '-f', 'mpegts', 'pipe:' - ] + logging.debug(f"{self.name}: idle video set to {self.idle_video}") + exit_code = 1 while exit_code > 0: self.stream = await asyncio.create_subprocess_exec( - *idle_stream_command, + *['ffmpeg', '-re', '-stream_loop', '-1', '-i', self.idle_video, + '-c:v', 'copy', + '-c:a', 'libmp3lame', '-ar', '44100', '-b:a', '8k', + '-bsf', 'dump_extra', '-f', 'mpegts', 'pipe:'], stdin=subprocess.DEVNULL, stdout=self.proxy_writer, stderr=subprocess.PIPE if DEBUG else subprocess.DEVNULL @@ -245,6 +246,20 @@ async def _start_stream(self): asyncio.create_task( self._log_stderr(self.stream, 'live_stream') ) + + if not self.resolution: + resolution = await self._get_resolution(stream) + if resolution: + logging.debug( + f"{self.name}: resolution found: {resolution}" + ) + self.resolution = resolution + else: + logging.warning( + f"{self.name}: failed to find resolution, setting " + f"default: {self._default_resolution}" + ) + self.resolution = self._default_resolution else: logging.debug(f"{self.name}: No stream available.") @@ -308,6 +323,62 @@ async def mqtt_control(self, payload): await self.event_loop.run_in_executor( None, self._arlo.request_snapshot) + async def _create_idle_video(self, image_path): + """ + Creates video from still image, with the cameras resolution. + Reverts to default on failure. + """ + output_path = f"{self.name}-idle.mp4" + + convert = await asyncio.create_subprocess_exec( + *['ffmpeg', '-loop', '1', '-i', image_path, + '-f', 'lavfi', '-i', 'anullsrc=r=16000:cl=mono', + '-c:v', 'libx264', '-c:a', 'mp2', '-t', '5', + '-pix_fmt', 'yuv420p', '-r', '24', '-vf', + f"scale={self.resolution[0]}:{self.resolution[1]}", + '-f', 'mpegts', '-y', output_path], + stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, + stderr=subprocess.PIPE + ) + + if DEBUG: + asyncio.create_task( + self._log_stderr(convert, 'create_idle') + ) + + exit_code = await convert.wait() + if exit_code > 0: + logging.warning( + f"{self.name}: failed to create idle video from {image_path}" + ) + output_path = None + + return output_path + + async def _get_resolution(self, stream): + probe = await asyncio.create_subprocess_exec( + *['ffprobe', '-v', 'error', '-select_streams', 'v:0', + '-show_entries', 'stream=width,height', '-of', 'csv=p=0', stream + ], + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL + ) + stdout, _ = await probe.communicate() + + result = False + + if probe.returncode == 0: + try: + pattern = r"(\d{3,4}),(\d{3,4})" + match = re.search(pattern, stdout.decode()) + if match: + result = (match.group(1), match.group(2)) + except UnicodeDecodeError: + pass + + return result + async def _log_stderr(self, stream, label): """ Continuously read from stderr and log the output. @@ -315,6 +386,7 @@ async def _log_stderr(self, stream, label): while True: try: line = await stream.stderr.readline() + if line: logging.debug( f"{self.name} - {label}: {line.decode().strip()}" diff --git a/device.py b/device.py index 50e19fd..7dcff64 100644 --- a/device.py +++ b/device.py @@ -23,7 +23,6 @@ async def run(self): Creates event channel between pyaarlo callbacks and async generator. Listens for and passes events to handler. """ - self.event_loop = asyncio.get_running_loop() event_get, event_put = self.create_sync_async_channel() self._arlo.add_attr_callback('*', event_put) asyncio.create_task(self._periodic_status_trigger()) diff --git a/main.py b/main.py index aee0e20..5d59227 100644 --- a/main.py +++ b/main.py @@ -15,9 +15,10 @@ IMAP_GRAB_ALL = config('IMAP_GRAB_ALL', default=False) MQTT_BROKER = config('MQTT_BROKER', default=None) FFMPEG_OUT = config('FFMPEG_OUT') +DEFAULT_RESOLUTION = config('DEFAULT_RESOLUTION', default=(1280, 768)) MOTION_TIMEOUT = config('MOTION_TIMEOUT', default=60, cast=int) STATUS_INTERVAL = config('STATUS_INTERVAL', default=120, cast=int) -LAST_IMAGE_IDLE = config('LAST_IMAGE_IDLE', default=False) +LAST_IMAGE_IDLE = config('LAST_IMAGE_IDLE', default=False, cast=bool) DEBUG = config('DEBUG', default=False, cast=bool) PYAARLO_BACKEND = config('PYAARLO_BACKEND', default=None) PYAARLO_REFRESH_DEVICES = config('PYAARLO_REFRESH_DEVICES', default=0, cast=int) @@ -69,7 +70,8 @@ async def main(): # Initialize cameras cameras = [Camera( - c, FFMPEG_OUT, MOTION_TIMEOUT, STATUS_INTERVAL, LAST_IMAGE_IDLE + c, FFMPEG_OUT, MOTION_TIMEOUT, STATUS_INTERVAL, LAST_IMAGE_IDLE, + DEFAULT_RESOLUTION ) for c in arlo.cameras] # Start both From 19274a5aeaaad67fbc46c6cfbc6024c1da4a05c9 Mon Sep 17 00:00:00 2001 From: kaffetorsk Date: Mon, 21 Oct 2024 00:33:51 +0200 Subject: [PATCH 08/11] add readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7257f0a..588e84a 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ PYAARLO_STORAGE_DIR: Pyaarlo storage_dir. Define it if you want to change the Py PYAARLO_ECDH_CURVE: Allowing you defining the ECDH CURVE to use during pyaarlo authentication IMAP_GRAB_ALL: Grabs all mails in the inbox, to avoid slow indexing problems (default: False) LAST_IMAGE_IDLE: Set last frame as idle image for the camera (default: False) +DEFAULT_RESOLUTION: Default resolution for the idle video (default: (1280, 768)) ``` ### Running ``` From 89d2b5e6ef9733ec16c390c965315f3c37af1593 Mon Sep 17 00:00:00 2001 From: kaffetorsk Date: Mon, 21 Oct 2024 12:20:11 +0200 Subject: [PATCH 09/11] add delete_after option for email codes --- README.md | 1 + main.py | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 588e84a..8aaac89 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ PYAARLO_STREAM_TIMEOUT: Pyaarlo backend event stream timeout (in seconds) (defau PYAARLO_STORAGE_DIR: Pyaarlo storage_dir. Define it if you want to change the Pyaarlo storage directory. (default determined by pyaarlo) PYAARLO_ECDH_CURVE: Allowing you defining the ECDH CURVE to use during pyaarlo authentication IMAP_GRAB_ALL: Grabs all mails in the inbox, to avoid slow indexing problems (default: False) +IMAP_DELETE_AFTER: Deletes mail after extracting code for 2fa (default: False) LAST_IMAGE_IDLE: Set last frame as idle image for the camera (default: False) DEFAULT_RESOLUTION: Default resolution for the idle video (default: (1280, 768)) ``` diff --git a/main.py b/main.py index 5d59227..22142e9 100644 --- a/main.py +++ b/main.py @@ -12,7 +12,8 @@ IMAP_HOST = config('IMAP_HOST') IMAP_USER = config('IMAP_USER') IMAP_PASS = config('IMAP_PASS') -IMAP_GRAB_ALL = config('IMAP_GRAB_ALL', default=False) +IMAP_GRAB_ALL = config('IMAP_GRAB_ALL', default=False, cast=bool) +IMAP_DELETE_AFTER = config('IMAP_DELETE_AFTER', default=False, cast=bool) MQTT_BROKER = config('MQTT_BROKER', default=None) FFMPEG_OUT = config('FFMPEG_OUT') DEFAULT_RESOLUTION = config('DEFAULT_RESOLUTION', default=(1280, 768)) @@ -45,7 +46,8 @@ async def main(): 'tfa_host': IMAP_HOST, 'tfa_username': IMAP_USER, 'tfa_password': IMAP_PASS, - 'tfa_grab_all': IMAP_GRAB_ALL + 'tfa_grab_all': IMAP_GRAB_ALL, + 'tfa_delete_after': IMAP_DELETE_AFTER } if PYAARLO_REFRESH_DEVICES: From f5cb028444a1ed87fa64eb97ed97560430e84bff Mon Sep 17 00:00:00 2001 From: kaffetorsk Date: Tue, 22 Oct 2024 17:58:12 +0200 Subject: [PATCH 10/11] add more keyframes --- camera.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/camera.py b/camera.py index d0f9da6..0796b04 100644 --- a/camera.py +++ b/camera.py @@ -334,7 +334,7 @@ async def _create_idle_video(self, image_path): *['ffmpeg', '-loop', '1', '-i', image_path, '-f', 'lavfi', '-i', 'anullsrc=r=16000:cl=mono', '-c:v', 'libx264', '-c:a', 'mp2', '-t', '5', - '-pix_fmt', 'yuv420p', '-r', '24', '-vf', + '-pix_fmt', 'yuv420p', '-r', '24', '-g', '24', '-vf', f"scale={self.resolution[0]}:{self.resolution[1]}", '-f', 'mpegts', '-y', output_path], stdin=subprocess.DEVNULL, From 4c9aa38cdc3b696eb40e56078469be742066b332 Mon Sep 17 00:00:00 2001 From: kaffetorsk Date: Tue, 22 Oct 2024 18:00:02 +0200 Subject: [PATCH 11/11] cleanup --- camera.py | 1 - 1 file changed, 1 deletion(-) diff --git a/camera.py b/camera.py index 0796b04..670991f 100644 --- a/camera.py +++ b/camera.py @@ -386,7 +386,6 @@ async def _log_stderr(self, stream, label): while True: try: line = await stream.stderr.readline() - if line: logging.debug( f"{self.name} - {label}: {line.decode().strip()}"