라이브 스트림
라이브 모니터링은 HLS 스트림에서 SSAI 광고 브레이크를 감시합니다. 서비스는 poll_interval_s 간격으로 매니페스트를 폴링하고, 새 광고 경계를 감지할 때마다 버스트(burst) 를 만듭니다. 버스트는 광고 브레이크 직전에 재생된 콘텐츠 세그먼트 segment_n개의 분석 결과입니다. 버스트마다 세그먼트별 장면(캡션, 트랜스크립트, 분위기, 브랜드, GARM 카테고리)과 버스트 단위 집계(요약, 장르, IAB 카테고리, GARM 평가)가 담깁니다.
모든 예시는 https://video.pyler.ai와 $VUF_API_KEY에 담긴 API 키를 사용합니다. 인증을 참고하세요.
스트림 등록
POST /api/v1/live/streams로 HLS 매니페스트 URL(마스터 또는 미디어 플레이리스트)을 등록합니다. 등록 시 매니페스트를 검증합니다. 접근할 수 없거나 유효하지 않은 플레이리스트는 422를, 이미 등록된 URL은 409를 반환합니다.
urlstringbodyrequiredHLS 매니페스트 URL입니다. 예: https://example.com/live/stream.m3u8
segment_nintegerbodydefault: 8버스트마다 수집할, 광고 브레이크 직전 콘텐츠 세그먼트 수입니다. 분석 구간을 결정합니다. 1–60.
poll_interval_sintegerbodydefault: 6매니페스트 폴링 간격(초)입니다. 1–3600.
- cURL
- Python
curl -X POST https://video.pyler.ai/api/v1/live/streams \
-H "Authorization: Bearer $VUF_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/live/stream.m3u8", "segment_n": 8, "poll_interval_s": 6}'
import os, requests
BASE = "https://video.pyler.ai/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['VUF_API_KEY']}"}
r = requests.post(f"{BASE}/live/streams", headers=HEADERS, json={
"url": "https://example.com/live/stream.m3u8",
"segment_n": 8,
"poll_interval_s": 6,
})
r.raise_for_status()
stream = r.json()
stream_id = stream["id"]
{
"id": "b7e2c4d6-1f3a-4b5c-9d8e-0a1b2c3d4e5f",
"url": "https://example.com/live/stream.m3u8",
"segment_n": 8,
"aggregate": true,
"status": "active",
"poll_interval_s": 6,
"namespace": null,
"org_id": "org_...",
"segments_seen": 0,
"error": null,
"created_at": "2026-10-20T09:00:00Z",
"updated_at": "2026-10-20T09:00:00Z"
}
스트림 레코드(LiveStreamOut):
statusstring현재 상태입니다. active, paused, stopped, error 중 하나입니다.
segments_seeninteger | null생성 이후 관찰한 미디어 세그먼트의 총수입니다.
errorstring | null마지막 오류 메시지입니다. status가 error일 때 채워집니다.
namespacestring | null스트림 목록을 조회할 때 namespace 필터로 사용할 수 있습니다.
레코드에는 url, segment_n, poll_interval_s, aggregate, org_id, created_at, updated_at도 포함됩니다.
목록 조회, 조회, 수정, 삭제
| 호출 | 동작 | 레퍼런스 |
|---|---|---|
GET /api/v1/live/streams | 조직에 등록된 스트림을 스트림 레코드 배열로 반환합니다. ?namespace= 필터는 선택입니다. | 라이브 스트림 목록 조회 |
GET /api/v1/live/streams/{stream_id} | id로 스트림 하나를 조회합니다. 없으면 404입니다. | 라이브 스트림 조회 |
PATCH /api/v1/live/streams/{stream_id} | 설정 일부를 수정하거나 상태를 전환합니다. | 라이브 스트림 수정 |
DELETE /api/v1/live/streams/{stream_id} | 모니터링을 멈추고 스트림을 삭제합니다. 204, 본문 없음. | 라이브 스트림 삭제 |
PATCH 본문에는 다음 필드 중 원하는 것만 넣을 수 있습니다.
urlstringbody새 HLS 매니페스트 URL입니다. 변경하면 새 매니페스트를 동기적으로 검증합니다. 접근할 수 없거나 유효하지 않은 플레이리스트는 422를 반환합니다.
segment_nintegerbody1–60.
poll_interval_sintegerbody1–3600.
statusstringbodyactive는 폴링을 재개하고, paused는 상태를 유지한 채 폴링을 멈추며, stopped는 스트림을 종료합니다.
- cURL
- Python
# Pause
curl -X PATCH https://video.pyler.ai/api/v1/live/streams/$STREAM_ID \
-H "Authorization: Bearer $VUF_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status": "paused"}'
# Resume with a wider analysis window
curl -X PATCH https://video.pyler.ai/api/v1/live/streams/$STREAM_ID \
-H "Authorization: Bearer $VUF_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status": "active", "segment_n": 12}'
# Delete
curl -X DELETE https://video.pyler.ai/api/v1/live/streams/$STREAM_ID \
-H "Authorization: Bearer $VUF_API_KEY"
requests.patch(f"{BASE}/live/streams/{stream_id}", headers=HEADERS,
json={"status": "paused"}).raise_for_status()
requests.patch(f"{BASE}/live/streams/{stream_id}", headers=HEADERS,
json={"status": "active", "segment_n": 12}).raise_for_status()
requests.delete(f"{BASE}/live/streams/{stream_id}", headers=HEADERS).raise_for_status()
스트림을 삭제하면 해당 스트림의 버스트와 장면도 모두 영구 삭제됩니다(cascade). 필요한 데이터는 먼저 조회해 두세요.
버스트
버스트(LiveBurstOut)는 광고 브레이크 하나 직전에 수집한 콘텐츠의 분석 결과입니다.
{
"id": "c1d2e3f4-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
"stream_id": "b7e2c4d6-1f3a-4b5c-9d8e-0a1b2c3d4e5f",
"boundary_sequence": 1842,
"break_detected_at": "2026-10-20T09:14:02Z",
"completed_at": "2026-10-20T09:14:21Z",
"status": "done",
"segment_count": 8,
"wall_ms": 18700,
"summary": "A studio panel discusses the evening's match before the break.",
"genre": ["sports"],
"iab_content_leaf": ["Sports"],
"iab_audience_interest_leaf": [],
"iab_audience_purchase_intent_leaf": [],
"iab_ad_product_leaf": [],
"garm_video": [],
"error": null,
"created_at": "2026-10-20T09:14:02Z",
"updated_at": "2026-10-20T09:14:21Z",
"scenes": [
{
"id": "d4e5f6a7-8b9c-4d0e-9f1a-2b3c4d5e6f70",
"segment_index": 0,
"segment_url": "https://example.com/live/seg1835.ts",
"duration_ms": 6000,
"caption": "Three presenters at a desk in front of a stadium backdrop.",
"transcript": "...and that brings us to tonight's line-up.",
"text_on_screen": ["LIVE"],
"valence": 3,
"arousal": 3,
"music_mood": null,
"audio_events": ["speech"],
"garm_categories": [],
"garm_reasoning": [],
"brands": [],
"product_categories_visible": [],
"people": [],
"locations": ["studio"],
"action": ["talking"],
"human_activity": ["presenting"],
"weather": null,
"download_ms": 240,
"caption_ms": 1900,
"status": "done",
"error": null
}
]
}
statusstring버스트 상태입니다. pending, running, done, failed 중 하나입니다.
break_detected_at / completed_atdate-time경계를 감지한 시각과 처리를 마친 시각입니다. 처리가 끝나기 전까지 completed_at은 null입니다.
wall_msinteger | null전체 처리에 걸린 실제 시간(밀리초)입니다.
summarystring | nullAI가 생성한 버스트 콘텐츠의 한 줄 요약입니다.
garm_videoGarmEntry[]버스트의 GARM 브랜드 세이프티 평가입니다. category, level(high / medium / low), reasoning, time_ranges로 구성됩니다.
scenesLiveSceneOut[]분석한 세그먼트 하나당 항목 하나입니다. segment_index, segment_url, duration_ms, caption, transcript, text_on_screen, valence, arousal, music_mood, audio_events, garm_categories, garm_reasoning, brands, product_categories_visible, people, locations, action, human_activity, weather, download_ms, caption_ms, status, error를 포함합니다.
버스트에는 id, stream_id, boundary_sequence, segment_count, genre, IAB leaf 목록, error, created_at, updated_at도 포함됩니다.
스트림 단위 버스트 엔드포인트
| 호출 | 동작 | 레퍼런스 |
|---|---|---|
GET /api/v1/live/streams/{stream_id}/bursts | 스트림의 모든 버스트를 최신순으로, 장면과 함께 반환합니다. ?since=(ISO 8601 date-time)를 지정하면 그 이후에 생성된 버스트만 반환합니다. | 버스트 목록 조회 |
GET /api/v1/live/streams/{stream_id}/bursts/latest | 가장 최근에 완료된 버스트를 반환합니다. 아직 완료된 버스트가 없으면 404입니다. | 최신 버스트 |
GET /api/v1/live/streams/{stream_id}/bursts/{burst_id} | 스트림 안의 버스트 하나를 반환합니다. 이 스트림에 속하지 않거나 존재하지 않으면 404입니다. | 버스트 조회 |
계정 단위 버스트 엔드포인트
| 호출 | 동작 | 레퍼런스 |
|---|---|---|
GET /api/v1/live/bursts/latest | 조직의 모든 스트림에서 가장 최근에 생성된 버스트를 배열로 반환합니다. ?since=(ISO 8601 date-time)로 생성 시각을 필터링합니다. | 최신 버스트 목록 |
GET /api/v1/live/bursts/{burst_id} | 어느 스트림에서 생성됐는지와 관계없이 id로 버스트를 조회합니다. | id로 버스트 조회 |
- cURL
- Python
# Latest completed burst for one stream
curl -s https://video.pyler.ai/api/v1/live/streams/$STREAM_ID/bursts/latest \
-H "Authorization: Bearer $VUF_API_KEY"
# Bursts created since a point in time, across all of your streams
curl -s "https://video.pyler.ai/api/v1/live/bursts/latest?since=2026-10-20T09:00:00Z" \
-H "Authorization: Bearer $VUF_API_KEY"
latest = requests.get(f"{BASE}/live/streams/{stream_id}/bursts/latest", headers=HEADERS)
if latest.status_code == 404:
print("no completed burst yet")
else:
burst = latest.json()
print(burst["summary"], [g["category"] for g in burst["garm_video"]])
recent = requests.get(f"{BASE}/live/bursts/latest", headers=HEADERS,
params={"since": "2026-10-20T09:00:00Z"}).json()
이벤트
GET /api/v1/live/streams/{stream_id}/events는 오래 유지되는 application/x-ndjson 스트림으로, 한 줄에 JSON 객체 하나씩 스트림 하나의 실시간 이벤트를 전달합니다. 응답 전체를 버퍼링하지 않는 클라이언트로 읽으세요. 각 줄에는 event 필드가 있습니다.
event | 의미 |
|---|---|
watching | 현재 스트림 상태 스냅숏입니다. |
break_detected | 새 광고 경계를 감지했습니다. |
scene | 세그먼트 분석이 끝났습니다. |
aggregate | 버스트 집계가 갱신됐습니다. |
burst_done | 버스트 처리가 끝났습니다. |
keepalive | 유휴 상태 하트비트입니다. 무시하세요. |
스트림이 stopped로 전환되거나 삭제되면 연결이 닫힙니다.
- cURL
- Python
curl -N https://video.pyler.ai/api/v1/live/streams/$STREAM_ID/events \
-H "Authorization: Bearer $VUF_API_KEY"
import json
with requests.get(f"{BASE}/live/streams/{stream_id}/events", headers=HEADERS, stream=True) as r:
r.raise_for_status()
for line in r.iter_lines():
if not line:
continue
evt = json.loads(line)
if evt["event"] == "keepalive":
continue
print(evt["event"], evt)
이벤트는 무언가가 언제 일어났는지 알려 주고, 데이터는 버스트에 있습니다. burst_done을 받으면 GET /api/v1/live/streams/{stream_id}/bursts/latest 또는 GET /api/v1/live/bursts/{burst_id}로 버스트를 조회하세요.
오류
| 상태 코드 | 발생 조건 |
|---|---|
404 | 알 수 없는 stream_id 또는 burst_id, 버스트가 속하지 않은 스트림 경로로 요청한 경우, 완료된 버스트가 없는데 bursts/latest를 호출한 경우입니다. |
409 | 이미 등록된 URL로 POST /api/v1/live/streams를 호출한 경우입니다. |
422 | 본문 검증에 실패했거나, 매니페스트 URL(생성 시 또는 새 url로 PATCH 시)에 접근할 수 없거나 유효한 플레이리스트가 아닌 경우입니다. |
모든 오류 본문은 오류에서 설명하는 공통 형식을 따릅니다.