Skip to content

feat: add snapshot FTP-to-cloud forwarder service - #277

Closed
walter-pl8rec wants to merge 6 commits into
masterfrom
tool/ftp-to-snapshot
Closed

feat: add snapshot FTP-to-cloud forwarder service#277
walter-pl8rec wants to merge 6 commits into
masterfrom
tool/ftp-to-snapshot

Conversation

@walter-pl8rec

Copy link
Copy Markdown

Adds a new snapshot/ subproject that runs an FTP server to receive Hikvision ANPR snapshot uploads (full-frame image, plate crop, and anpr.xml metadata) and forwards the matching JPEG to the Plate Recognizer Snapshot Cloud API via /v1/plate-reader/.

Includes pyftpdlib-based FTP server, environment-driven configuration, example env file, and uv-managed dependencies.

Adds a new snapshot/ subproject that runs an FTP server to receive
Hikvision ANPR snapshot uploads (full-frame image, plate crop, and
anpr.xml metadata) and forwards the matching JPEG to the Plate
Recognizer Snapshot Cloud API via /v1/plate-reader/.

Includes pyftpdlib-based FTP server, environment-driven configuration,
example env file, and uv-managed dependencies.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a Python-based FTP server bridge that forwards Hikvision ANPR snapshots to the Plate Recognizer Snapshot Cloud API. Key feedback includes optimizing HTTP performance by utilizing a shared requests.Session for connection pooling, adding a defensive check to ignore empty files, reordering the shutdown sequence to close the FTP server before stopping the thread pool executor, and replacing the placeholder description in pyproject.toml.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread snapshot/main.py Outdated
Comment thread snapshot/main.py Outdated
Comment thread snapshot/main.py Outdated
Comment thread snapshot/ftp-to-snapshot-cloud/main.py
Comment thread snapshot/pyproject.toml Outdated
walter-pl8rec and others added 5 commits June 23, 2026 19:27
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new snapshot/ftp-to-snapshot-cloud subproject that runs a small FTP server (via pyftpdlib) to receive Hikvision ANPR snapshot uploads and forward matching JPEGs to the Plate Recognizer Snapshot /v1/plate-reader/ endpoint (Cloud by default, optional SDK mode).

Changes:

  • Introduces a new standalone Python service (main.py) with env-driven configuration for FTP ingest + background HTTP forwarding.
  • Adds documentation (README.md) and example configuration (.env.example) describing setup, networking, and operational behavior.
  • Adds uv project metadata (pyproject.toml, uv.lock) and local ignores (.gitignore) for the subproject.

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
snapshot/ftp-to-snapshot-cloud/main.py Implements FTP receive + suffix-based filtering + async forwarding to Snapshot endpoint.
snapshot/ftp-to-snapshot-cloud/README.md Documents deployment/configuration and camera/firewall setup.
snapshot/ftp-to-snapshot-cloud/.env.example Provides sample environment configuration for Cloud/SDK modes and FTP settings.
snapshot/ftp-to-snapshot-cloud/pyproject.toml Declares project metadata, dependencies, and Python requirement.
snapshot/ftp-to-snapshot-cloud/uv.lock Locks Python dependencies for uv installs.
snapshot/ftp-to-snapshot-cloud/.gitignore Adds subproject-local ignores (venv, build artifacts).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +68 to +70
if regions:
data["regions"] = regions
if camera_id:
Comment on lines +79 to +82
files = {"upload": (file_path.name, fp, "image/jpeg")}
response = session.post(
url, headers=headers, data=data, files=files, timeout=timeout
)
Comment on lines +83 to +85
except requests.RequestException as exc:
log.error("Snapshot API request failed for %s: %s", file_path.name, exc)
return
Comment on lines +123 to +126
log.warning("Ignoring empty matching file: %s", name)
return
log.info("Forwarding %s to Snapshot Cloud", name)
except OSError as exc:
Comment on lines +200 to +201
config_raw = _env_str("CONFIG_JSON", "")
config = json.loads(config_raw) if config_raw.strip() else None
Comment on lines +162 to +165
port = _env_int("FTP_PORT", 2121)
user = _env_str("FTP_USER", "camera")
password = _env_str("FTP_PASSWORD", "camera")
root = Path(_env_str("FTP_ROOT", "./uploads")).resolve()
Comment on lines +52 to +63
def forward_to_snapshot(
file_path: Path,
*,
url: str,
token: str,
regions: list[str] | None,
camera_id: str | None,
mmc: bool,
config: dict | None,
timeout: float,
) -> None:
"""Upload ``file_path`` to a Snapshot (Cloud or SDK) plate-reader endpoint."""
Comment on lines +1 to +10
# Python-generated files
__pycache__/
*.py[oc]
build/
dist/
wheels/
*.egg-info

# Virtual environments
.venv
Comment on lines +1 to +6
[project]
name = "ftp-to-snapshot-cloud"
version = "0.1.0"
description = "FTP server that forwards Hikvision ANPR snapshots to the Plate Recognizer Snapshot Cloud API"
requires-python = ">=3.13"
dependencies = [
Comment on lines +108 to +116
## Requirements

* **Python 3.13 or newer** (declared in `pyproject.toml`).
* Either **[uv](https://docs.astral.sh/uv/)** (recommended, a `uv.lock` is
shipped) or **pip** + a virtual environment.
* Network access to your target Snapshot endpoint:
* **Cloud** — outbound HTTPS to `api.platerecognizer.com`.
* **SDK** — outbound HTTP to the host where the SDK is running (default
`localhost:8080`; override with `SNAPSHOT_URL`).
@marcbelmont

Copy link
Copy Markdown
Collaborator

Let's focus on the http option instead. It's simpler to manage for the end user.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants