initial POC

This commit is contained in:
Dark98
2026-09-06 15:14:21 +00:00
commit f9c52293bb
9 changed files with 564 additions and 0 deletions
+80
View File
@@ -0,0 +1,80 @@
# Moonraker Timelapse Docker Template
This service watches a Klipper/Moonraker printer, captures a JPEG whenever the reported toolhead Z position increases by a configured layer height, and renders the captured frames to an H.264 MP4 after a successful print. Snapshot directories and videos are named with the print filename and UTC start date/time, for example `benchy_20260905-210500` and `benchy_20260905-210500.mp4`.
## Quick start
1. Copy `.env.example` to `.env`.
2. Set `MOONRAKER_URL` and `CAMERA_SNAPSHOT_URL` to the endpoints used by your printer and Mainsail.
3. Start the service:
```sh
docker compose up --build -d
```
4. Open the panel at `http://localhost:8080`.
5. Find rendered videos under `./timelapses`.
Storage is organized with videos in the root timelapse folder and snapshots in a matching print folder:
```text
timelapses/
├── benchy_20260905-210500.mp4
└── benchy_20260905-210500/
├── frame-000000.jpg
└── frame-000001.jpg
```
The panel is also available when running locally with `./run.sh` at `http://localhost:8080`. Set `WEB_PORT` to use another port. It shows Moonraker connectivity, printer state, current Z height, the latest captured snapshot, and downloadable completed videos. The JSON endpoints are `/api/status` and `/api/videos`; the latest JPEG is available at `/api/snapshot`.
To build the image directly instead of using Compose:
```sh
./build.sh
```
The default image is `moonraker-timelapse:latest`. Override it when publishing or testing a specific version:
```sh
IMAGE_NAME=registry.example.com/you/timelapse IMAGE_TAG=0.1.0 ./build.sh
```
For local development without Docker, install Python 3 and `ffmpeg`, then run:
```sh
./run.sh
```
The script loads `.env` when present, defaults `OUTPUT_DIR` to `./timelapses`, and uses `127.0.0.1:7125` as the local Moonraker default. Set `MOONRAKER_URL` and `CAMERA_SNAPSHOT_URL` in `.env` when the printer or camera is on another host.
The default camera URL is compatible with common MJPEG camera configurations using `?action=snapshot`. For a camera that exposes a different still-image endpoint, set `CAMERA_SNAPSHOT_URL` accordingly.
### Render settings
Render behavior can be configured in `.env`:
| Variable | Default | Purpose |
| --- | --- | --- |
| `VIDEO_FPS` | `12` | Playback frame rate. |
| `VIDEO_DUPLICATE_END_FRAMES` | `0` | Copies of the final snapshot appended to hold the end of the video. |
| `VIDEO_CODEC` | `libx264` | FFmpeg video encoder. |
| `VIDEO_PRESET` | `medium` | Encoder speed/compression preset. |
| `VIDEO_CRF` | `18` | H.264 quality; lower values produce larger, higher-quality files. |
| `VIDEO_PIXEL_FORMAT` | `yuv420p` | Output pixel format for broad player compatibility. |
For example, `VIDEO_FPS=24` and `VIDEO_DUPLICATE_END_FRAMES=48` creates a two-second hold on the final snapshot.
## How layer detection works
The worker polls Moonraker's `print_stats` and `gcode_move` objects. When slicer layer metadata (`print_stats.info.current_layer`) is available, it captures once per reported layer. Otherwise it falls back to detecting Z increases using `MIN_LAYER_HEIGHT_MM`; the default poll interval is `0.5` seconds, but layer metadata is recommended because a slow poll can miss a short Z move.
## Development smoke test
The Python worker uses only the standard library. Validate syntax locally with:
```sh
python3 -m py_compile app.py
docker compose config
```
The service intentionally treats Moonraker and camera failures as recoverable polling errors, so a printer or camera reboot does not stop the container.