initial POC
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user