Skip to content

Commit 613cc30

Browse files
jstabenowclaude
andcommitted
Add v2.12.0
Add AsyncClient.v3_cluster_events_log_stream for POST /api/v3/cluster/events/log. Core routes that path to the same cluster.LogEvents handler as /api/v3/cluster/events (http/server.go), so it is an alias with identical SSE format and LogEventFilter semantics. The path is absent from core-openapi.json, like /cluster/events/process. Add the optional hls field to SessionCollectorActiveSession with the new models SessionCollectorActiveSessionHls, ...HlsVariant, ...HlsSegments and ...HlsBandwidth, covering the per-session HLS stats (variants, segment counters, http_status, bandwidth min/max/avg) that newer Core versions report. All fields are optional, so payloads from older Cores without hls keep validating. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NeLTDVX4HzCvZkD5trYf82
1 parent 9be77bc commit 613cc30

12 files changed

Lines changed: 226 additions & 4 deletions

‎CHANGELOG.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
Changelog
22
---------
33

4+
## 2.12.0
5+
6+
- Add `AsyncClient.v3_cluster_events_log_stream` for `POST /api/v3/cluster/events/log` (endpoint was missing; Core routes it to the same `cluster.LogEvents` handler as `/api/v3/cluster/events`, so it is an alias with identical SSE format and `LogEventFilter` semantics)
7+
- Add `hls` to `SessionCollectorActiveSession` (optional): per-session HLS stats reported by newer Core versions, with the new models `SessionCollectorActiveSessionHls`, `SessionCollectorActiveSessionHlsVariant`, `SessionCollectorActiveSessionHlsSegments` and `SessionCollectorActiveSessionHlsBandwidth`
8+
49
## 2.11.2
510

611
- Add official support for Python 3.14 (classifier); verified: import + full unit suite pass on 3.11–3.14

‎README.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ Streaming methods (async only):
176176

177177
- `v3_events_stream(filters=…, frame=True, model=None)` → `POST /api/v3/events`
178178
- `v3_cluster_events_stream(filters=…)` → `POST /api/v3/cluster/events`
179+
- `v3_cluster_events_log_stream(filters=…)` → `POST /api/v3/cluster/events/log` (Core alias of `/api/v3/cluster/events`)
179180
- `v3_cluster_events_process_stream(filters=…)` → `POST /api/v3/cluster/events/process`
180181

181182
```python
@@ -197,7 +198,8 @@ asyncio.run(main())
197198
Notes:
198199

199200
- **Filter models:** `LogEventFilter` for the log-event streams
200-
(`/api/v3/events`, `/api/v3/cluster/events`); `ProcessEventFilter` (filter by
201+
(`/api/v3/events`, `/api/v3/cluster/events`, `/api/v3/cluster/events/log`);
202+
`ProcessEventFilter` (filter by
201203
`type`, `domain`, `pid`, `core_id`) for `/api/v3/cluster/events/process`. Each
202204
filter value is a **case-insensitive, unanchored regex** (e.g. `type="progress"`,
203205
`type="progress|report"`); multiple fields are AND-combined. Raw `dict` filters are
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"""Streaming endpoint: ``POST /api/v3/cluster/events/log``.
2+
3+
Endless SSE stream of cluster log events. Registered on ``AsyncClient`` as an
4+
async-generator method (see ``_add_stream_method``).
5+
6+
Core routes this path to the same handler as ``POST /api/v3/cluster/events``
7+
(``cluster.LogEvents``), so it is an explicit alias of ``v3_cluster_events_stream``
8+
with identical wire format and filter semantics (``LogEventFilter``).
9+
"""
10+
11+
from ...models import Client
12+
from ._stream import serialize_filters, stream_events
13+
14+
15+
def _build_request(client: Client, filters=None):
16+
return "POST", f"{client.base_url}/api/v3/cluster/events/log", serialize_filters(filters)
17+
18+
19+
async def asyncio_stream(client: Client, *, filters=None, frame: bool = True, model=None):
20+
method, url, body = _build_request(client, filters=filters)
21+
async for item in stream_events(client, method, url, json=body, frame=frame):
22+
if model is not None and frame:
23+
event_type, data = item
24+
yield event_type, model.model_validate_json(data)
25+
else:
26+
yield item

‎core_client/base/models/v3/__init__.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,10 @@
161161

162162
from .rtmp import Rtmp
163163

164+
from .session_collector_active_session_hls_variant import SessionCollectorActiveSessionHlsVariant
165+
from .session_collector_active_session_hls_segments import SessionCollectorActiveSessionHlsSegments
166+
from .session_collector_active_session_hls_bandwidth import SessionCollectorActiveSessionHlsBandwidth
167+
from .session_collector_active_session_hls import SessionCollectorActiveSessionHls
164168
from .session_collector_active_session import SessionCollectorActiveSession
165169
from .session_collector_active import SessionCollectorActive
166170
from .session_collector_summary import SessionCollectorSummary

‎core_client/base/models/v3/session_collector_active_session.py‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from pydantic import BaseModel
2-
from typing import Union
2+
from typing import Optional, Union
3+
4+
from . import SessionCollectorActiveSessionHls
35

46

57
class SessionCollectorActiveSession(BaseModel):
@@ -15,6 +17,7 @@ class SessionCollectorActiveSession(BaseModel):
1517
"bytes_tx": 0,
1618
"bandwidth_rx_kbit": 1931.2,
1719
"bandwidth_tx_kbit": 0
20+
+ "hls": SessionCollectorActiveSessionHls
1821
}
1922
"""
2023

@@ -28,3 +31,4 @@ class SessionCollectorActiveSession(BaseModel):
2831
bytes_tx: int
2932
bandwidth_rx_kbit: float
3033
bandwidth_tx_kbit: float
34+
hls: SessionCollectorActiveSessionHls | None = None
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from pydantic import BaseModel
2+
from typing import Dict, List, Optional
3+
4+
from . import (
5+
SessionCollectorActiveSessionHlsBandwidth,
6+
SessionCollectorActiveSessionHlsSegments,
7+
SessionCollectorActiveSessionHlsVariant,
8+
)
9+
10+
11+
class SessionCollectorActiveSessionHls(BaseModel):
12+
"""
13+
{
14+
"hls_variants": [SessionCollectorActiveSessionHlsVariant],
15+
"hls_segments": SessionCollectorActiveSessionHlsSegments,
16+
"http_status": {
17+
"200": 2954
18+
},
19+
"bandwidth_tx_bits": SessionCollectorActiveSessionHlsBandwidth
20+
}
21+
"""
22+
23+
hls_variants: list[SessionCollectorActiveSessionHlsVariant] | None = None
24+
hls_segments: SessionCollectorActiveSessionHlsSegments | None = None
25+
http_status: dict[str, int] | None = None
26+
bandwidth_tx_bits: SessionCollectorActiveSessionHlsBandwidth | None = None
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from pydantic import BaseModel
2+
from typing import Optional
3+
4+
5+
class SessionCollectorActiveSessionHlsBandwidth(BaseModel):
6+
"""
7+
{
8+
"min": 20901924.340143416,
9+
"max": 2540419186.6611648,
10+
"avg": 1001351629.3989341
11+
}
12+
"""
13+
14+
min: float | None = None
15+
max: float | None = None
16+
avg: float | None = None
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from pydantic import BaseModel
2+
from typing import Optional
3+
4+
5+
class SessionCollectorActiveSessionHlsSegments(BaseModel):
6+
"""
7+
{
8+
"requests": 1447,
9+
"failed": 0,
10+
"too_slow": 0,
11+
"retries": 3,
12+
"too_late": 0,
13+
"sequence_gaps": 17,
14+
"last": 1788469276
15+
}
16+
"""
17+
18+
requests: int | None = None
19+
failed: int | None = None
20+
too_slow: int | None = None
21+
retries: int | None = None
22+
too_late: int | None = None
23+
sequence_gaps: int | None = None
24+
last: int | None = None
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from pydantic import BaseModel
2+
from typing import Optional
3+
4+
5+
class SessionCollectorActiveSessionHlsVariant(BaseModel):
6+
"""
7+
{
8+
"path": "/memfs/.../live/0.m3u8",
9+
"active": true,
10+
"switches": 2,
11+
"bandwidth_bits": 2393600,
12+
"resolution": "1280x720",
13+
"codecs": ""
14+
}
15+
"""
16+
17+
path: str | None = None
18+
active: bool | None = None
19+
switches: int | None = None
20+
bandwidth_bits: int | None = None
21+
resolution: str | None = None
22+
codecs: str | None = None

‎setup.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
setup(
2121
name="datarhei-mediacore-client",
22-
version="2.11.2",
22+
version="2.12.0",
2323
url="https://github.com/datarhei/core-client-python",
2424
project_urls={
2525
"Source": "https://github.com/datarhei/core-client-python",

0 commit comments

Comments
 (0)