mirror of
https://github.com/ivuorinen/aeonview.git
synced 2026-03-13 19:58:39 +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:
|
1. Clone the repository:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/your-username/aeonview.git
|
git clone https://github.com/ivuorinen/aeonview.git
|
||||||
cd aeonview
|
cd aeonview
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ This guide will help you get started.
|
|||||||
```bash
|
```bash
|
||||||
python3 -m venv venv
|
python3 -m venv venv
|
||||||
source venv/bin/activate
|
source venv/bin/activate
|
||||||
pip install -r dev-requirements.txt
|
pip install -r requirements.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Install pre-commit hooks:
|
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]
|
[![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
|
## Features
|
||||||
|
|
||||||
- Timelapse image capture (`--mode image`)
|
- Timelapse image capture (`--mode image`)
|
||||||
- Video generation (`--mode video`)
|
- Video generation (`--mode video`)
|
||||||
- Support for daily, monthly, yearly video runs *(daily implemented)*
|
- Support for daily, monthly, yearly video runs *(daily implemented)*
|
||||||
- Uses `ffmpeg` and `curl`
|
- Uses `ffmpeg` and Python `requests`
|
||||||
- Fully tested with `pytest`
|
- Fully tested with `pytest`
|
||||||
- Linting and formatting via `ruff`
|
- Linting and formatting via `ruff`
|
||||||
- Pre-commit hooks and CI-ready
|
- Pre-commit hooks and CI-ready
|
||||||
@@ -29,10 +29,9 @@ Low quality sample: [aeonview 2min preview/Tampere Jan. 2008][sample]
|
|||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- Python 3.13+
|
- Python 3.13+
|
||||||
- `ffmpeg` and `curl` (system tools)
|
- `ffmpeg` (system tool)
|
||||||
- lots of hard drive space
|
- lots of hard drive space
|
||||||
- Optional: `pyenv` for managing Python versions
|
- Optional: `pyenv` for managing Python versions
|
||||||
(see `pyenv_requirements`)
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ from __future__ import annotations
|
|||||||
import argparse
|
import argparse
|
||||||
import hashlib
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
import os
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
@@ -240,7 +239,10 @@ class AeonViewVideos:
|
|||||||
|
|
||||||
if not self.simulate:
|
if not self.simulate:
|
||||||
logging.info("Running ffmpeg command: %s", " ".join(ffmpeg_cmd))
|
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)
|
AeonViewHelpers.mkdir_p(output_dir)
|
||||||
subprocess.run(ffmpeg_cmd, check=True)
|
subprocess.run(ffmpeg_cmd, check=True)
|
||||||
logging.info(
|
logging.info(
|
||||||
@@ -481,7 +483,7 @@ class AeonViewApp:
|
|||||||
self.base_path, self.args.project
|
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)
|
logging.error("Project path %s does not exist.", project_path)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|||||||
@@ -174,9 +174,10 @@ def test_download_image_failure(mock_get):
|
|||||||
avi.download_image(destination)
|
avi.download_image(destination)
|
||||||
|
|
||||||
|
|
||||||
@mock.patch("aeonview.AeonViewHelpers.mkdir_p")
|
|
||||||
@mock.patch("subprocess.run")
|
@mock.patch("subprocess.run")
|
||||||
def test_generate_daily_video(mock_subprocess_run, mock_mkdir_p):
|
def test_generate_daily_video(mock_subprocess_run):
|
||||||
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
|
project_path = Path(tmp).resolve()
|
||||||
args = argparse.Namespace(
|
args = argparse.Namespace(
|
||||||
simulate=False,
|
simulate=False,
|
||||||
fps=10,
|
fps=10,
|
||||||
@@ -184,14 +185,18 @@ def test_generate_daily_video(mock_subprocess_run, mock_mkdir_p):
|
|||||||
month="04",
|
month="04",
|
||||||
year="2025"
|
year="2025"
|
||||||
)
|
)
|
||||||
avv = AeonViewVideos(default_test_path, args)
|
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:
|
with mock.patch("aeonview.logging.info") as log:
|
||||||
avv.generate_daily_video()
|
avv.generate_daily_video()
|
||||||
last_call_args = log.call_args_list[-1][0]
|
last_call_args = log.call_args_list[-1][0]
|
||||||
assert last_call_args[0] == "%s: %s"
|
assert last_call_args[0] == "%s: %s"
|
||||||
assert last_call_args[1] == AeonViewMessages.VIDEO_GENERATION_SUCCESS
|
assert last_call_args[1] == AeonViewMessages.VIDEO_GENERATION_SUCCESS
|
||||||
assert last_call_args[2] == default_test_path / "vid/2025-04/01.mp4"
|
assert last_call_args[2] == (
|
||||||
|
project_path / "vid" / "2025-04" / "01.mp4"
|
||||||
|
)
|
||||||
mock_mkdir_p.assert_called()
|
mock_mkdir_p.assert_called()
|
||||||
mock_subprocess_run.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