Files
wzdj/lib/api/streamer-studio.ts
卡若 ea3a589f0b
Some checks failed
CI / notice (push) Has been cancelled
chore: sync docs, gitea wiki assets, templates
Made-with: Cursor
2026-04-14 18:44:44 +08:00

112 lines
3.7 KiB
TypeScript

/** 主播工作台:当前登录用户对应的主播档案与开/关播 */
export type StreamerSelfData = {
streamerId: string
name: string
title: string
game: string
isLive: boolean
room: {
id: string
title: string
status: string
pullPlaybackUrl: string
} | null
}
export async function fetchStreamerSelf(): Promise<{ ok: true; data: StreamerSelfData } | { ok: false; status: number; message: string }> {
const res = await fetch("/api/streamer/me", { credentials: "include", cache: "no-store" })
const json = (await res.json().catch(() => ({}))) as {
success?: boolean
data?: StreamerSelfData
message?: string
}
if (!res.ok || !json.success || !json.data) {
return { ok: false, status: res.status, message: json.message ?? "无法加载主播档案" }
}
return { ok: true, data: json.data }
}
export type LiveCloudVendorId = "tencent" | "aliyun" | "qiniu" | "volcengine" | "generic"
export type LiveCloudUrlsPayload = {
vendor: LiveCloudVendorId
configured: boolean
streamName: string
expiresAt: string
setupHint?: string
docs?: { label: string; href: string }[]
push?: { rtmp?: string; srt?: string; note?: string }
play?: { hls?: string; flv?: string; rtmp?: string; note?: string }
}
export async function fetchStreamerCloudUrls(
vendor: LiveCloudVendorId,
ttlSec = 7200,
): Promise<{ ok: true; data: LiveCloudUrlsPayload } | { ok: false; message: string }> {
const res = await fetch(
`/api/streamer/live/cloud?vendor=${encodeURIComponent(vendor)}&ttl=${encodeURIComponent(String(ttlSec))}`,
{ credentials: "include", cache: "no-store" },
)
const json = (await res.json().catch(() => ({}))) as {
success?: boolean
data?: LiveCloudUrlsPayload
message?: string
}
if (!res.ok || !json.success || !json.data) {
return { ok: false, message: json.message ?? "无法获取云厂商地址" }
}
return { ok: true, data: json.data }
}
export async function postStreamerLiveAction(
action: "start" | "stop",
payload?: { title?: string; pullPlaybackUrl?: string | null },
): Promise<{ ok: boolean; message?: string; data?: Record<string, unknown> }> {
const res = await fetch("/api/streamer/live", {
method: "POST",
headers: { "Content-Type": "application/json" },
credentials: "include",
body: JSON.stringify({ action, ...payload }),
})
const json = (await res.json().catch(() => ({}))) as {
success?: boolean
message?: string
data?: Record<string, unknown>
}
if (!res.ok || !json.success) {
return { ok: false, message: json.message ?? "操作失败" }
}
return { ok: true, data: json.data }
}
export type AudienceViewerRow = {
viewerKey: string
displayLabel: string
viewerType: "user" | "anonymous"
lastTouchAt: string
maxWatchMarkSec: number
likeCount: number
commentCount: number
shareCount: number
giftCount: number
engagePercent: number
recentTouches: { eventName: string; at: string; detail?: string }[]
}
export async function fetchAudienceInsights(): Promise<
| { ok: true; data: { streamerId: string; windowDays: number; viewers: AudienceViewerRow[]; totalViewers: number } }
| { ok: false; status: number; message: string }
> {
const res = await fetch("/api/streamer/audience-insights", { credentials: "include", cache: "no-store" })
const json = (await res.json().catch(() => ({}))) as {
success?: boolean
data?: { streamerId: string; windowDays: number; viewers: AudienceViewerRow[]; totalViewers: number }
message?: string
}
if (!res.ok || !json.success || !json.data) {
return { ok: false, status: res.status, message: json.message ?? "无法加载观众触达" }
}
return { ok: true, data: json.data }
}