8 Commits
Author SHA1 Message Date
cristhian 76e1fb8bb0 document the https clone URL
validate / validate (push) Successful in 59m12s
ssh:// cannot authenticate under dora-cli 1.0.1, which fetches through libgit2
with no credential callback, so a consumer wiring this node by `git:` needs the
anonymous https URL.
2026-09-15 06:29:39 -03:00
cristhian 43759f7e00 lint markdown recursively
validate / validate (push) Successful in 59m14s
`scan *.md` only matched the repository root, so a file added under any
subdirectory would have stopped being checked without the CI step noticing.
Scanning recursively follows the files instead of the layout, and
--respect-gitignore keeps it off .venv and .dora.
2026-09-15 06:20:11 -03:00
cristhian 69ad074a79 document configuring the node from the dataflow
validate / validate (push) Successful in 59m14s
Every setting is an environment variable, which reads fine for one and badly
for six: the README's table implied the only way to set them was to export them
around `dora run`. A node takes an `env:` map in the dataflow, applied to both
the build and the run, which keeps configuration with the wiring rather than in
whatever shell happened to start it. The README now shows a populated node.

A node's `env:` wins over the environment the dataflow was started in, and a
variable it does not set still passes through. That is why the block in
dataflow.yml is present but commented out: CI passes CAPTURE_FRAMES in through
the shell, and pinning it there would silently override the runner.
2026-09-15 06:18:38 -03:00
cristhian 7628a8515b discover a camera when none is configured
validate / validate (push) Successful in 59m14s
CAPTURE_PATH defaulted to index 0, which is a coin flip on a machine with one
camera. A UVC camera exposes a second device node for its metadata stream --
/dev/video1 beside /dev/video0 -- and that node opens happily and never yields
an image, so the lowest device number is wrong about as often as it is right.

With neither CAMERA_ID nor CAPTURE_PATH set, the node now opens each
/dev/video* in numeric order and keeps the first that actually delivers a
frame. Reading one is the test, because opening is not evidence.

Discovery finding nothing falls back to index 0 rather than raising, which
keeps the failure in one place: the first read, where CI publishes a
placeholder and a normal run raises with a message naming the device.

CAMERA_ID is still worth preferring on a robot. Neither an index nor discovery
order is stable across reboots or across which port a camera is in; a by-id
name is.
2026-09-15 06:16:14 -03:00
cristhian dc700106c5 move the workflow to .github/workflows
validate / validate (push) Successful in 59m7s
act looks in .github/workflows by default, and running it locally before a
commit is one of this repository's rules; keeping the workflow where act cannot
see it meant every invocation needed -W. Gitea Actions reads both locations, so
only one of them may exist or a push fires the run twice.
2026-09-15 06:13:53 -03:00
cristhian 6f76074709 name no consumer of this node
validate / validate (push) Successful in 59m15s
A source that documents itself through a specific downstream node depends on
that node to be understood, and that node already depends on this one for
frames. The README pointed at one by name and the CI workflow still carried a
comment about the dataflow that wired it, which no longer exists.

The output is documented by its type instead: wire `image` into anything that
takes std/media/v1/Image. PREVIEW covers seeing the frames without naming
anyone. Generic words like "a viewer" or "a consumer" stay, since they describe
a role rather than a repository.
2026-09-15 06:13:06 -03:00
cristhian dc3960809a build with uv, and preview locally instead of a second dataflow
validate / validate (push) Successful in 59m16s
The build command was `pip install .`, which is what upstream and the dora docs
use because `--uv` rewrites it. Spelling it `uv pip install .` means the
dataflow never installs through pip at all, whether or not `--uv` was passed.
The cost is that the node then needs a virtualenv to install into, so it must
be run with `--uv`; the README says so.

The second dataflow is gone. It existed to wire this node into dora-image-view
just to see the frames, which pulled another repository over the network and
needed a tag there. PREVIEW=true now opens a local window of the frames as they
are published, so checking a camera, an encoding or a flip needs this
repository alone. It shows the frame after the flip and the resize, so what is
on screen is what is sent, and it never opens under CI, where there is no
display.

That a source node can display its own output overlaps with what a viewer node
does. The overlap is the point: the alternative was a second repository in the
loop before you can tell whether a camera works.
2026-09-15 06:09:12 -03:00
cristhian 3c0e0f327d add dora-webcam node
validate / validate (push) Successful in 59m15s
Captures frames from a V4L2 camera and publishes them as std/media/v1/Image:
an Arrow struct of width, height, encoding and data, so a receiver needs
nothing from the message metadata. For a compressed encoding the recorded
dimensions are the image's, not the length of the payload.

Based on opencv-video-capture from dora-hub, with three changes. The output is
the typed image struct rather than a flat UInt8 buffer plus metadata. Camera
selection by id reads /dev/v4l/by-id/ only; the upstream macOS system_profiler
and Windows PowerShell enumeration is not carried. CAPTURE_FRAMES bounds a run
so a test ends on its own, in place of upstream's ten-second CI cutoff -- a
timeout would have made the CI step exit non-zero.

CI=true keeps a failed capture from raising, publishing a labelled black frame
instead, which is what lets dataflow.yml run on a machine with no camera.

Two dataflows, run one at a time, as dora has no cross-dataflow wiring:
dataflow.yml is the camera alone and the CI gate; dataflow_view.yml wires it
into dora-image-view and is not run by CI, since it fetches that node over the
network and needs a tag to exist there.
2026-09-15 06:04:25 -03:00