fix(cctv): update DGT Spain camera endpoint to etraffic.dgt.es (#413)

DGT migrated its public traffic site in 2026: the old image path
infocar.dgt.es/etraffic/data/camaras/{id}.jpg now 302-redirects to the
new etrafficWEB SPA and no longer serves JPEGs, so every DGT camera
showed FEED UNAVAILABLE (proxy and direct source both failed).

New working pattern: https://etraffic.dgt.es/camarasEtraffic/{id}.jpg

- cctv_pipeline.py: DGTNationalIngestor media_url + probe Referer
- routers/cctv.py & main.py: allowlist etraffic.dgt.es + dgt-spain proxy profile
This commit is contained in:
Javier Andreo Zapata
2026-06-25 08:57:39 +08:00
committed by GitHub
parent 0690d94c37
commit f30ffe2c77
4 changed files with 12 additions and 9 deletions
+3 -2
View File
@@ -8140,6 +8140,7 @@ _CCTV_PROXY_ALLOWED_HOSTS = {
"tripcheck.com", # Oregon DOT / TripCheck
"www.tripcheck.com",
"infocar.dgt.es", # Spain DGT
"etraffic.dgt.es", # Spain DGT (etrafficWEB cameras host, 2026)
"informo.madrid.es", # Madrid
"webcams2.asfinag.at", # Austria ASFINAG motorway cameras
"odo.asfinag.at", # ASFINAG catalog API host
@@ -8336,14 +8337,14 @@ def _cctv_proxy_profile_for_url(target_url: str) -> _CCTVProxyProfile:
cache_seconds=30,
headers={"Accept": "image/avif,image/webp,image/apng,image/*,*/*;q=0.8"},
)
if host == "infocar.dgt.es":
if host in {"infocar.dgt.es", "etraffic.dgt.es"}:
return _CCTVProxyProfile(
name="dgt-spain",
timeout=(5.0, 8.0),
cache_seconds=60,
headers={
"Accept": "image/avif,image/webp,image/apng,image/*,*/*;q=0.8",
"Referer": "https://infocar.dgt.es/",
"Referer": "https://etraffic.dgt.es/",
},
)
if host == "informo.madrid.es":
+3 -2
View File
@@ -46,6 +46,7 @@ _CCTV_PROXY_ALLOWED_HOSTS = {
"tripcheck.com",
"www.tripcheck.com",
"infocar.dgt.es",
"etraffic.dgt.es",
"informo.madrid.es",
"webcams2.asfinag.at",
"odo.asfinag.at",
@@ -158,10 +159,10 @@ def _cctv_proxy_profile_for_url(target_url: str) -> _CCTVProxyProfile:
if host in {"tripcheck.com", "www.tripcheck.com"}:
return _CCTVProxyProfile(name="odot-tripcheck", timeout=(_CCTV_PROXY_CONNECT_TIMEOUT_S, 12.0), cache_seconds=30,
headers={"Accept": "image/avif,image/webp,image/apng,image/*,*/*;q=0.8"})
if host == "infocar.dgt.es":
if host in {"infocar.dgt.es", "etraffic.dgt.es"}:
return _CCTVProxyProfile(name="dgt-spain", timeout=(_CCTV_PROXY_CONNECT_TIMEOUT_S, 8.0), cache_seconds=60,
headers={"Accept": "image/avif,image/webp,image/apng,image/*,*/*;q=0.8",
"Referer": "https://infocar.dgt.es/"})
"Referer": "https://etraffic.dgt.es/"})
if host == "informo.madrid.es":
return _CCTVProxyProfile(name="madrid-city", timeout=(_CCTV_PROXY_CONNECT_TIMEOUT_S, 12.0), cache_seconds=30,
headers={"Accept": "image/avif,image/webp,image/apng,image/*,*/*;q=0.8",
+4 -3
View File
@@ -1031,7 +1031,8 @@ out body;
# ---------------------------------------------------------------------------
# DGT Spain — National Road Cameras
# ---------------------------------------------------------------------------
# Image URL pattern confirmed working: infocar.dgt.es/etraffic/data/camaras/{id}.jpg
# Image URL pattern confirmed working: etraffic.dgt.es/camarasEtraffic/{id}.jpg
# (DGT migrated 2026: old infocar.dgt.es/etraffic/data/camaras path now 302->etrafficWEB)
# Source: DGT (Dirección General de Tráfico) — public open data (Ley 37/2007).
# Author credit: Alborz Nazari (github.com/AlborzNazari) — PR #91
@@ -1065,10 +1066,10 @@ class DGTNationalIngestor(BaseCCTVIngestor):
cameras = []
probe_headers = {
"Accept": "image/avif,image/webp,image/apng,image/*,*/*;q=0.8",
"Referer": "https://infocar.dgt.es/",
"Referer": "https://etraffic.dgt.es/",
}
for cam_id, lat, lon, description in self.KNOWN_CAMERAS:
media_url = f"https://infocar.dgt.es/etraffic/data/camaras/{cam_id}.jpg"
media_url = f"https://etraffic.dgt.es/camarasEtraffic/{cam_id}.jpg"
if not _media_url_reachable(media_url, timeout=6, headers=probe_headers):
continue
cameras.append({
+2 -2
View File
@@ -314,7 +314,7 @@ def test_cctv_proxy_profiles_are_source_specific():
tfl = main._cctv_proxy_profile_for_url("https://s3-eu-west-1.amazonaws.com/jamcams.tfl.gov.uk/00001.mp4")
austin = main._cctv_proxy_profile_for_url("https://cctv.austinmobility.io/image/316.jpg")
georgia = main._cctv_proxy_profile_for_url("https://511ga.org/map/Cctv/22378")
spain = main._cctv_proxy_profile_for_url("https://infocar.dgt.es/etraffic/data/camaras/1050.jpg")
spain = main._cctv_proxy_profile_for_url("https://etraffic.dgt.es/camarasEtraffic/1050.jpg")
assert tfl.name == "tfl-jamcam"
assert tfl.headers["Accept"].startswith("video/mp4")
@@ -324,7 +324,7 @@ def test_cctv_proxy_profiles_are_source_specific():
assert georgia.timeout == (5.0, 12.0)
assert georgia.headers["Referer"] == "https://511ga.org/cctv"
assert spain.name == "dgt-spain"
assert spain.headers["Referer"] == "https://infocar.dgt.es/"
assert spain.headers["Referer"] == "https://etraffic.dgt.es/"
def test_cctv_proxy_preserves_upstream_http_status(monkeypatch):