mirror of
https://github.com/ivuorinen/aeonview.git
synced 2026-03-13 08:58:27 +00:00
fix: implement all CodeRabbit review comments (#8)
This commit is contained in:
4
.github/CONTRIBUTING.md
vendored
4
.github/CONTRIBUTING.md
vendored
@@ -8,7 +8,7 @@ This guide will help you get started.
|
||||
1. Clone the repository:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/your-username/aeonview.git
|
||||
git clone https://github.com/ivuorinen/aeonview.git
|
||||
cd aeonview
|
||||
```
|
||||
|
||||
@@ -17,7 +17,7 @@ This guide will help you get started.
|
||||
```bash
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
pip install -r dev-requirements.txt
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
3. Install pre-commit hooks:
|
||||
|
||||
@@ -14,14 +14,14 @@ Includes developer tooling and tests.
|
||||
|
||||
[![CI][ci-b]][ci-l] [![ruff][cc-b]][cc-l] [![MIT][lm-b]][lm-l]
|
||||
|
||||
Low quality sample: [aeonview 2min preview/Tampere Jan. 2008][sample]
|
||||
Low-quality sample: [aeonview 2min preview/Tampere Jan. 2008][sample]
|
||||
|
||||
## Features
|
||||
|
||||
- Timelapse image capture (`--mode image`)
|
||||
- Video generation (`--mode video`)
|
||||
- Support for daily, monthly, yearly video runs *(daily implemented)*
|
||||
- Uses `ffmpeg` and `curl`
|
||||
- Uses `ffmpeg` and Python `requests`
|
||||
- Fully tested with `pytest`
|
||||
- Linting and formatting via `ruff`
|
||||
- Pre-commit hooks and CI-ready
|
||||
@@ -29,10 +29,9 @@ Low quality sample: [aeonview 2min preview/Tampere Jan. 2008][sample]
|
||||
## Requirements
|
||||
|
||||
- Python 3.13+
|
||||
- `ffmpeg` and `curl` (system tools)
|
||||
- `ffmpeg` (system tool)
|
||||
- lots of hard drive space
|
||||
- Optional: `pyenv` for managing Python versions
|
||||
(see `pyenv_requirements`)
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ from __future__ import annotations
|
||||
import argparse
|
||||
import hashlib
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime, timedelta
|
||||
@@ -240,7 +239,10 @@ class AeonViewVideos:
|
||||
|
||||
if not self.simulate:
|
||||
logging.info("Running ffmpeg command: %s", " ".join(ffmpeg_cmd))
|
||||
if not os.path.exists(output_dir):
|
||||
if not input_dir.exists():
|
||||
logging.error("Input directory %s does not exist", input_dir)
|
||||
sys.exit(1)
|
||||
if not output_dir.exists():
|
||||
AeonViewHelpers.mkdir_p(output_dir)
|
||||
subprocess.run(ffmpeg_cmd, check=True)
|
||||
logging.info(
|
||||
@@ -481,7 +483,7 @@ class AeonViewApp:
|
||||
self.base_path, self.args.project
|
||||
)
|
||||
|
||||
if not os.path.exists(project_path):
|
||||
if not project_path.exists():
|
||||
logging.error("Project path %s does not exist.", project_path)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@@ -174,25 +174,30 @@ def test_download_image_failure(mock_get):
|
||||
avi.download_image(destination)
|
||||
|
||||
|
||||
@mock.patch("aeonview.AeonViewHelpers.mkdir_p")
|
||||
@mock.patch("subprocess.run")
|
||||
def test_generate_daily_video(mock_subprocess_run, mock_mkdir_p):
|
||||
args = argparse.Namespace(
|
||||
simulate=False,
|
||||
fps=10,
|
||||
day="01",
|
||||
month="04",
|
||||
year="2025"
|
||||
)
|
||||
avv = AeonViewVideos(default_test_path, args)
|
||||
with mock.patch("aeonview.logging.info") as log:
|
||||
avv.generate_daily_video()
|
||||
last_call_args = log.call_args_list[-1][0]
|
||||
assert last_call_args[0] == "%s: %s"
|
||||
assert last_call_args[1] == AeonViewMessages.VIDEO_GENERATION_SUCCESS
|
||||
assert last_call_args[2] == default_test_path / "vid/2025-04/01.mp4"
|
||||
|
||||
mock_mkdir_p.assert_called()
|
||||
def test_generate_daily_video(mock_subprocess_run):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
project_path = Path(tmp).resolve()
|
||||
args = argparse.Namespace(
|
||||
simulate=False,
|
||||
fps=10,
|
||||
day="01",
|
||||
month="04",
|
||||
year="2025"
|
||||
)
|
||||
avv = AeonViewVideos(project_path, args)
|
||||
# Create input directory so the existence check passes
|
||||
(project_path / "img" / "2025-04" / "01").mkdir(parents=True)
|
||||
with mock.patch("aeonview.AeonViewHelpers.mkdir_p") as mock_mkdir_p:
|
||||
with mock.patch("aeonview.logging.info") as log:
|
||||
avv.generate_daily_video()
|
||||
last_call_args = log.call_args_list[-1][0]
|
||||
assert last_call_args[0] == "%s: %s"
|
||||
assert last_call_args[1] == AeonViewMessages.VIDEO_GENERATION_SUCCESS
|
||||
assert last_call_args[2] == (
|
||||
project_path / "vid" / "2025-04" / "01.mp4"
|
||||
)
|
||||
mock_mkdir_p.assert_called()
|
||||
mock_subprocess_run.assert_called()
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
class Response:
|
||||
def __init__(self, status_code=200):
|
||||
self.status_code = status_code
|
||||
|
||||
def iter_content(self, chunk_size=1024):
|
||||
return iter([])
|
||||
|
||||
def get(url, stream=False, timeout=0):
|
||||
raise NotImplementedError("requests.get is not implemented")
|
||||
Reference in New Issue
Block a user