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
|
||||
|
||||
def _fetch() -> tuple[bytes, str]:
|
||||
return fetch_hls_upstream(upstream)
|
||||
return fetch_hls_upstream(upstream, query=request.url.query)
|
||||
|
||||
try:
|
||||
body, ctype = await asyncio.to_thread(_fetch)
|
||||
|
||||
@@ -400,8 +400,18 @@ class HlsPreviewManager:
|
||||
return f"{base}/{sub}" if sub else base
|
||||
|
||||
|
||||
def fetch_hls_upstream(url: str) -> tuple[bytes, str]:
|
||||
req = Request(url, headers={"User-Agent": "operation-room-monitor-hls-proxy/1.0"})
|
||||
def append_upstream_query(upstream_url: str, query: str) -> str:
|
||||
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
|
||||
body = resp.read()
|
||||
ctype = resp.headers.get_content_type() or "application/octet-stream"
|
||||
|
||||
@@ -14,6 +14,7 @@ from app.services.hls_preview import (
|
||||
HlsPreviewManager,
|
||||
HlsPreviewSession,
|
||||
_mediamtx_config_lines,
|
||||
append_upstream_query,
|
||||
attached_upstream_connect_candidates,
|
||||
docker_publish_bind_host,
|
||||
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"]
|
||||
|
||||
|
||||
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:
|
||||
HlsPreviewManager._active = HlsPreviewSession(
|
||||
upstream_base="http://127.0.0.1:18888",
|
||||
|
||||
Reference in New Issue
Block a user