fix(hls): forward MediaMTX LL-HLS session query in proxy
MediaMTX v1.18+ requires ?session= on sub-playlists; the API proxy was dropping query params and causing 401 on video1_stream.m3u8 fetches. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -337,7 +337,7 @@ async def hls_preview_proxy(
|
|||||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="unknown camera") from exc
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="unknown camera") from exc
|
||||||
|
|
||||||
def _fetch() -> tuple[bytes, str]:
|
def _fetch() -> tuple[bytes, str]:
|
||||||
return fetch_hls_upstream(upstream)
|
return fetch_hls_upstream(upstream, query=request.url.query)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
body, ctype = await asyncio.to_thread(_fetch)
|
body, ctype = await asyncio.to_thread(_fetch)
|
||||||
|
|||||||
@@ -400,8 +400,18 @@ class HlsPreviewManager:
|
|||||||
return f"{base}/{sub}" if sub else base
|
return f"{base}/{sub}" if sub else base
|
||||||
|
|
||||||
|
|
||||||
def fetch_hls_upstream(url: str) -> tuple[bytes, str]:
|
def append_upstream_query(upstream_url: str, query: str) -> str:
|
||||||
req = Request(url, headers={"User-Agent": "operation-room-monitor-hls-proxy/1.0"})
|
q = (query or "").strip().lstrip("?")
|
||||||
|
if not q:
|
||||||
|
return upstream_url
|
||||||
|
base = upstream_url.rstrip("/")
|
||||||
|
sep = "&" if "?" in base else "?"
|
||||||
|
return f"{base}{sep}{q}"
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_hls_upstream(url: str, *, query: str = "") -> tuple[bytes, str]:
|
||||||
|
target = append_upstream_query(url, query)
|
||||||
|
req = Request(target, headers={"User-Agent": "operation-room-monitor-hls-proxy/1.0"})
|
||||||
with urlopen(req, timeout=15) as resp: # noqa: S310
|
with urlopen(req, timeout=15) as resp: # noqa: S310
|
||||||
body = resp.read()
|
body = resp.read()
|
||||||
ctype = resp.headers.get_content_type() or "application/octet-stream"
|
ctype = resp.headers.get_content_type() or "application/octet-stream"
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ from app.services.hls_preview import (
|
|||||||
HlsPreviewManager,
|
HlsPreviewManager,
|
||||||
HlsPreviewSession,
|
HlsPreviewSession,
|
||||||
_mediamtx_config_lines,
|
_mediamtx_config_lines,
|
||||||
|
append_upstream_query,
|
||||||
attached_upstream_connect_candidates,
|
attached_upstream_connect_candidates,
|
||||||
docker_publish_bind_host,
|
docker_publish_bind_host,
|
||||||
normalize_upstream_base,
|
normalize_upstream_base,
|
||||||
@@ -75,6 +76,38 @@ def test_hls_preview_ensure_pull(hls_client: TestClient) -> None:
|
|||||||
assert "index.m3u8" in body["cameras"][0]["playlist_url"]
|
assert "index.m3u8" in body["cameras"][0]["playlist_url"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_append_upstream_query() -> None:
|
||||||
|
assert (
|
||||||
|
append_upstream_query("http://mediamtx-hls:8888/or-cam-01/video1_stream.m3u8", "session=abc")
|
||||||
|
== "http://mediamtx-hls:8888/or-cam-01/video1_stream.m3u8?session=abc"
|
||||||
|
)
|
||||||
|
assert append_upstream_query("http://host/a.m3u8?foo=1", "session=abc") == (
|
||||||
|
"http://host/a.m3u8?foo=1&session=abc"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_hls_proxy_forwards_session_query(hls_client: TestClient) -> None:
|
||||||
|
HlsPreviewManager._active = HlsPreviewSession(
|
||||||
|
upstream_base="http://127.0.0.1:18888",
|
||||||
|
path_by_camera={"or-cam-01": "or-cam-01"},
|
||||||
|
mode="attached",
|
||||||
|
)
|
||||||
|
captured: list[str] = []
|
||||||
|
|
||||||
|
def _fake_fetch(url: str, *, query: str = "") -> tuple[bytes, str]:
|
||||||
|
captured.append(append_upstream_query(url, query))
|
||||||
|
return (b"#EXTM3U\n", "application/vnd.apple.mpegurl")
|
||||||
|
|
||||||
|
with patch("app.api.fetch_hls_upstream", side_effect=_fake_fetch):
|
||||||
|
r = hls_client.get(
|
||||||
|
"/internal/demo/hls-preview/or-cam-01/video1_stream.m3u8?session=abc-123"
|
||||||
|
)
|
||||||
|
assert r.status_code == 200
|
||||||
|
assert captured == [
|
||||||
|
"http://127.0.0.1:18888/or-cam-01/video1_stream.m3u8?session=abc-123"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def test_hls_proxy_rewrites_playlist(hls_client: TestClient) -> None:
|
def test_hls_proxy_rewrites_playlist(hls_client: TestClient) -> None:
|
||||||
HlsPreviewManager._active = HlsPreviewSession(
|
HlsPreviewManager._active = HlsPreviewSession(
|
||||||
upstream_base="http://127.0.0.1:18888",
|
upstream_base="http://127.0.0.1:18888",
|
||||||
|
|||||||
Reference in New Issue
Block a user