From 2c31cb4550606a25b4a6f5904f65b1e2524232fc Mon Sep 17 00:00:00 2001 From: patrykorwat Date: Fri, 21 Aug 2026 11:46:03 +0000 Subject: [PATCH] fix(axelera): accept scheme-less host URLs in model.path is_url() required an http(s) scheme, so a bare host like www.example.com/model.axm fell through to the filesystem branch and raised FileNotFoundError, blocking detector init on a valid remote model (reviewer P1). A value matching a leading dotted host (or localhost) with a path is now recognised as a URL and http:// is prepended at fetch time; relative paths and preset names behave as before. --- frigate/detectors/plugins/axelera.py | 30 +++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/frigate/detectors/plugins/axelera.py b/frigate/detectors/plugins/axelera.py index 1a20cc89a4..0aa3134925 100644 --- a/frigate/detectors/plugins/axelera.py +++ b/frigate/detectors/plugins/axelera.py @@ -27,6 +27,7 @@ Model configuration follows the hardware-backend convention: import logging import os import queue +import re import threading import time from typing import Literal @@ -78,9 +79,31 @@ TOKEN_NO_ID = 0 # ----------------- Utility Functions ----------------- # +# Matches a scheme-less host URL: host([:port])/path, e.g. www.example.com/model.axm +# or localhost:8000/model.axm (bare filenames like foo.axm and relative paths like +# models/foo.axm have no leading dotted host and stay local paths / presets). +_HOST_URL_RE = re.compile( + r"^(localhost|[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z]{2,})(:[0-9]+)?/" +) + + def is_url(value: str) -> bool: - """Return True for well-formed http(s) URLs (scheme required).""" - return value.startswith(("http://", "https://")) + """Return True for http(s) URLs and scheme-less host URLs. + + A bare host such as ``www.example.com/model.axm`` (no scheme) is a usable + URL too: rejecting it would raise FileNotFoundError on a valid remote + model. ``_url_with_scheme`` prepends ``http://`` before any fetch. + """ + return value.startswith(("http://", "https://")) or bool( + _HOST_URL_RE.match(value) + ) + + +def _url_with_scheme(value: str) -> str: + """Prepend http:// to a scheme-less host URL (vetted by is_url).""" + if value.startswith(("http://", "https://")): + return value + return f"http://{value}" def _looks_like_path(value: str) -> bool: @@ -105,7 +128,8 @@ def _resolve_network(model_path, model_url) -> str: if not os.path.isfile(destination): logger.info("axelera: downloading model from %s", model_url) try: - ModelDownloader.download_from_url(model_url, destination) + download_url = _url_with_scheme(model_url) + ModelDownloader.download_from_url(download_url, destination) except Exception as exc: raise RuntimeError( f"Failed to download model from {model_url}: {exc}"