's'
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { Alert } from 'react-native'
|
||||
import { useAuth } from '@/context/AuthContext'
|
||||
import { useBind } from '@/context/BindContext'
|
||||
import { apiFetch, apiJson } from '@/lib/api'
|
||||
import { reportLiveControlEvent } from '@/lib/liveTelemetry'
|
||||
import type { GpuCaps, YoutubeIniModel } from '@/lib/pcTypes'
|
||||
import { getMultiChannelPro, setMultiChannelPro } from '@/lib/storage'
|
||||
import { isObsScript, isTiktokScript, isYoutubeScript, pm2NameFromScript } from '@/lib/scriptUtils'
|
||||
@@ -72,6 +74,9 @@ function pickProcessForEntries(filtered: Entry[], multi: boolean): string {
|
||||
|
||||
export function useRecorder() {
|
||||
const { bind } = useBind()
|
||||
const { pbToken, ensureCanControlLive, session } = useAuth()
|
||||
const pbTokRef = useRef(pbToken)
|
||||
pbTokRef.current = pbToken
|
||||
const [entries, setEntries] = useState<Entry[]>([])
|
||||
const [currentProcess, setCurrentProcess] = useState('')
|
||||
const [statusMeta, setStatusMeta] = useState<{ line: string; variant: StatusVariant }>({
|
||||
@@ -135,7 +140,7 @@ export function useRecorder() {
|
||||
useEffect(() => {
|
||||
if (!bindKey) return
|
||||
let cancelled = false
|
||||
void apiFetch(bindRef.current!, '/host/gpu_status')
|
||||
void apiFetch(bindRef.current!, '/host/gpu_status', { pbToken: pbTokRef.current })
|
||||
.then((r) => (r.ok ? r.json() : Promise.reject(new Error(String(r.status)))))
|
||||
.then((j: GpuCaps) => {
|
||||
if (!cancelled)
|
||||
@@ -166,7 +171,7 @@ export function useRecorder() {
|
||||
const loadProcessMonitor = useCallback(async () => {
|
||||
const b = bindRef.current
|
||||
if (!b) throw new Error('尚未绑定 PC')
|
||||
const res = await apiFetch(b, '/process_monitor')
|
||||
const res = await apiFetch(b, '/process_monitor', { pbToken: pbTokRef.current })
|
||||
if (!res.ok) throw new Error(String(res.status))
|
||||
const data = (await res.json()) as { entries?: { script: string; label?: string; pm2?: string }[] }
|
||||
const raw: Entry[] = (data.entries || []).map((e) => ({
|
||||
@@ -186,6 +191,7 @@ export function useRecorder() {
|
||||
const d = await apiJson<{ ok?: boolean; error?: string; channels?: typeof proChannelsList }>(
|
||||
b,
|
||||
'/relay_pro/channels',
|
||||
{ pbToken: pbTokRef.current },
|
||||
)
|
||||
if (!d.ok) {
|
||||
setProChannelsError(d.error || '无法加载 Pro 频道')
|
||||
@@ -220,7 +226,7 @@ export function useRecorder() {
|
||||
}
|
||||
setYoutubeStatus('正在将当前配置同步到多频道…')
|
||||
try {
|
||||
const res = await apiFetch(b, '/relay_pro/channels')
|
||||
const res = await apiFetch(b, '/relay_pro/channels', { pbToken: pbTokRef.current })
|
||||
const d = (await res.json()) as { ok?: boolean; channels?: { id: string }[]; error?: string }
|
||||
if (!d.ok || !d.channels?.length) throw new Error(d.error || '无法加载频道列表')
|
||||
const firstId = d.channels[0].id
|
||||
@@ -231,6 +237,7 @@ export function useRecorder() {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ channelId: firstId, keys, activeIndex: 0 }),
|
||||
pbToken: pbTokRef.current,
|
||||
})
|
||||
const kj = (await keyRes.json()) as { error?: string }
|
||||
if (!keyRes.ok) throw new Error(kj.error || '同步密钥失败')
|
||||
@@ -241,6 +248,7 @@ export function useRecorder() {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ content: urlText }),
|
||||
pbToken: pbTokRef.current,
|
||||
},
|
||||
)
|
||||
const uj = (await urlRes.json()) as { error?: string }
|
||||
@@ -258,6 +266,7 @@ export function useRecorder() {
|
||||
options: ytIniModel.options,
|
||||
header: ytIniModel.header || '',
|
||||
}),
|
||||
pbToken: pbTokRef.current,
|
||||
},
|
||||
)
|
||||
} catch {
|
||||
@@ -282,6 +291,7 @@ export function useRecorder() {
|
||||
const res = await apiFetch(
|
||||
bind,
|
||||
`${endpoint}?process=${encodeURIComponent(currentProcess)}`,
|
||||
{ pbToken: pbTokRef.current },
|
||||
)
|
||||
if (!res.ok) throw new Error(`连接失败 ${res.status}`)
|
||||
const data = (await res.json()) as { content?: string }
|
||||
@@ -310,6 +320,7 @@ export function useRecorder() {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ content }),
|
||||
pbToken: pbTokRef.current,
|
||||
},
|
||||
)
|
||||
if (!res.ok) throw new Error(`保存失败 ${res.status}`)
|
||||
@@ -330,6 +341,7 @@ export function useRecorder() {
|
||||
const res = await apiFetch(
|
||||
bind,
|
||||
`/youtube/ini_model?process=${encodeURIComponent(currentProcess)}`,
|
||||
{ pbToken: pbTokRef.current },
|
||||
)
|
||||
const data = (await res.json()) as Partial<YoutubeIniModel> & { error?: string }
|
||||
if (!res.ok) throw new Error(data.error || `HTTP ${res.status}`)
|
||||
@@ -375,6 +387,7 @@ export function useRecorder() {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(modelToSave),
|
||||
pbToken: pbTokRef.current,
|
||||
},
|
||||
)
|
||||
const data = (await res.json()) as { error?: string; message?: string }
|
||||
@@ -404,6 +417,7 @@ export function useRecorder() {
|
||||
keys: keysOne,
|
||||
activeIndex: 0,
|
||||
}),
|
||||
pbToken: pbTokRef.current,
|
||||
})
|
||||
const data = (await res.json()) as { error?: string; message?: string }
|
||||
if (!res.ok) throw new Error(data.error || `HTTP ${res.status}`)
|
||||
@@ -450,6 +464,7 @@ export function useRecorder() {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ content: ed.urlText }),
|
||||
pbToken: pbTokRef.current,
|
||||
},
|
||||
)
|
||||
const data = (await res.json()) as { error?: string; message?: string }
|
||||
@@ -469,6 +484,7 @@ export function useRecorder() {
|
||||
const ur = await apiFetch(
|
||||
b,
|
||||
`/relay_pro/url_config?channel_id=${encodeURIComponent(proChannelId)}`,
|
||||
{ pbToken: pbTokRef.current },
|
||||
)
|
||||
if (!ur.ok) {
|
||||
setUrlStatus('读取失败')
|
||||
@@ -518,9 +534,17 @@ export function useRecorder() {
|
||||
}
|
||||
const proc = procForPm2(processOverride)
|
||||
const isStatus = action === 'status'
|
||||
if (!isStatus) setLoadingAction(action)
|
||||
if (!isStatus) {
|
||||
if (action === 'start' || action === 'restart') {
|
||||
const allowed = await ensureCanControlLive()
|
||||
if (!allowed) return
|
||||
}
|
||||
setLoadingAction(action)
|
||||
}
|
||||
try {
|
||||
const res = await apiFetch(bindRef.current, `/${action}?process=${encodeURIComponent(proc)}`)
|
||||
const res = await apiFetch(bindRef.current, `/${action}?process=${encodeURIComponent(proc)}`, {
|
||||
pbToken: pbTokRef.current,
|
||||
})
|
||||
if (!res.ok) throw new Error('无法连接到本机服务')
|
||||
const data = (await res.json()) as Record<string, unknown>
|
||||
|
||||
@@ -551,6 +575,15 @@ export function useRecorder() {
|
||||
const actionLabel =
|
||||
action === 'start' ? '开始' : action === 'stop' ? '停止' : action === 'restart' ? '重新开始' : action
|
||||
setLogText(`已对「${who}」执行:${actionLabel}\n\n${out}`)
|
||||
if (action === 'start' || action === 'restart' || action === 'stop') {
|
||||
void reportLiveControlEvent(
|
||||
bindRef.current,
|
||||
pbTokRef.current,
|
||||
session?.record?.id,
|
||||
proc,
|
||||
action,
|
||||
)
|
||||
}
|
||||
const statusProc = processOverride ?? proc
|
||||
setTimeout(() => void runPm2Action('status', statusProc), 1500)
|
||||
}
|
||||
@@ -561,18 +594,22 @@ export function useRecorder() {
|
||||
if (!isStatus) setLoadingAction(null)
|
||||
}
|
||||
},
|
||||
[bindRef, procForPm2, getEntry],
|
||||
[bindRef, procForPm2, getEntry, ensureCanControlLive, session?.record?.id],
|
||||
)
|
||||
|
||||
const runProChannelStart = useCallback(
|
||||
async (channelId: string) => {
|
||||
if (!channelId) return
|
||||
const allowed = await ensureCanControlLive()
|
||||
if (!allowed) return
|
||||
const pm = proChannelPm2Process(channelId)
|
||||
setLoadingAction('start')
|
||||
try {
|
||||
const saved = await saveProChannelKeysFor(channelId)
|
||||
if (!saved) return
|
||||
const res = await apiFetch(bindRef.current!, `/start?process=${encodeURIComponent(pm)}`)
|
||||
const res = await apiFetch(bindRef.current!, `/start?process=${encodeURIComponent(pm)}`, {
|
||||
pbToken: pbTokRef.current,
|
||||
})
|
||||
if (!res.ok) throw new Error('无法连接到本机服务')
|
||||
const data = (await res.json()) as Record<string, unknown>
|
||||
const e = getEntry(pm)
|
||||
@@ -584,6 +621,7 @@ export function useRecorder() {
|
||||
.filter((x): x is string => typeof x === 'string')
|
||||
.join('\n') || '已完成'
|
||||
setLogText(`已对「${who}」执行:开始直播\n\n${out}`)
|
||||
void reportLiveControlEvent(bindRef.current, pbTokRef.current, session?.record?.id, pm, 'start')
|
||||
setTimeout(() => void runPm2Action('status', pm), 1500)
|
||||
} catch (err) {
|
||||
setLogText(`操作失败:${err instanceof Error ? err.message : String(err)}`)
|
||||
@@ -591,18 +629,22 @@ export function useRecorder() {
|
||||
setLoadingAction(null)
|
||||
}
|
||||
},
|
||||
[getEntry, runPm2Action, saveProChannelKeysFor],
|
||||
[getEntry, runPm2Action, saveProChannelKeysFor, ensureCanControlLive, session?.record?.id],
|
||||
)
|
||||
|
||||
const runProChannelRestart = useCallback(
|
||||
async (channelId: string) => {
|
||||
if (!channelId) return
|
||||
const allowed = await ensureCanControlLive()
|
||||
if (!allowed) return
|
||||
const pm = proChannelPm2Process(channelId)
|
||||
setLoadingAction('restart')
|
||||
try {
|
||||
const saved = await saveProChannelKeysFor(channelId)
|
||||
if (!saved) return
|
||||
const res = await apiFetch(bindRef.current!, `/restart?process=${encodeURIComponent(pm)}`)
|
||||
const res = await apiFetch(bindRef.current!, `/restart?process=${encodeURIComponent(pm)}`, {
|
||||
pbToken: pbTokRef.current,
|
||||
})
|
||||
if (!res.ok) throw new Error('无法连接到本机服务')
|
||||
const data = (await res.json()) as Record<string, unknown>
|
||||
const e = getEntry(pm)
|
||||
@@ -614,6 +656,7 @@ export function useRecorder() {
|
||||
.filter((x): x is string => typeof x === 'string')
|
||||
.join('\n') || '已完成'
|
||||
setLogText(`已对「${who}」执行:重新开始\n\n${out}`)
|
||||
void reportLiveControlEvent(bindRef.current, pbTokRef.current, session?.record?.id, pm, 'restart')
|
||||
setTimeout(() => void runPm2Action('status', pm), 1500)
|
||||
} catch (err) {
|
||||
setLogText(`操作失败:${err instanceof Error ? err.message : String(err)}`)
|
||||
@@ -621,7 +664,7 @@ export function useRecorder() {
|
||||
setLoadingAction(null)
|
||||
}
|
||||
},
|
||||
[getEntry, runPm2Action, saveProChannelKeysFor],
|
||||
[getEntry, runPm2Action, saveProChannelKeysFor, ensureCanControlLive, session?.record?.id],
|
||||
)
|
||||
|
||||
const submitNewChannelDraft = useCallback(async () => {
|
||||
@@ -637,6 +680,7 @@ export function useRecorder() {
|
||||
name: `频道 ${proChannelsList.length + 1}`,
|
||||
youtubeKey: key,
|
||||
}),
|
||||
pbToken: pbTokRef.current,
|
||||
})
|
||||
const d = (await res.json()) as { error?: string; message?: string; channelId?: string }
|
||||
if (!res.ok) throw new Error(d.error || `HTTP ${res.status}`)
|
||||
@@ -675,6 +719,7 @@ export function useRecorder() {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ channelId }),
|
||||
pbToken: pbTokRef.current,
|
||||
})
|
||||
const d = (await res.json()) as { error?: string; message?: string }
|
||||
if (!res.ok) throw new Error(d.error || `HTTP ${res.status}`)
|
||||
@@ -848,6 +893,7 @@ export function useRecorder() {
|
||||
const d = await apiJson<{ ok?: boolean; error?: string; channels?: typeof proChannelsList }>(
|
||||
bind,
|
||||
'/relay_pro/channels',
|
||||
{ pbToken: pbTokRef.current },
|
||||
)
|
||||
if (cancelled) return
|
||||
if (!d.ok) {
|
||||
@@ -878,7 +924,7 @@ export function useRecorder() {
|
||||
let cancelled = false
|
||||
const poll = async () => {
|
||||
try {
|
||||
const r = await apiFetch(bind, '/relay_pro/live_status')
|
||||
const r = await apiFetch(bind, '/relay_pro/live_status', { pbToken: pbTokRef.current })
|
||||
if (!r.ok || cancelled) return
|
||||
const j = (await r.json()) as ProRelayStatusPayload
|
||||
if (!cancelled) setProRelayStatus(j)
|
||||
@@ -910,6 +956,7 @@ export function useRecorder() {
|
||||
const dr = await apiFetch(
|
||||
b,
|
||||
`/relay_pro/channel_detail?channel_id=${encodeURIComponent(c.id)}`,
|
||||
{ pbToken: pbTokRef.current },
|
||||
)
|
||||
if (!dr.ok) continue
|
||||
const cd = (await dr.json()) as {
|
||||
@@ -951,6 +998,7 @@ export function useRecorder() {
|
||||
const ur = await apiFetch(
|
||||
bind,
|
||||
`/relay_pro/url_config?channel_id=${encodeURIComponent(cid)}`,
|
||||
{ pbToken: pbTokRef.current },
|
||||
)
|
||||
if (!ur.ok || cancelled) return
|
||||
const j = (await ur.json()) as { content?: string }
|
||||
|
||||
Reference in New Issue
Block a user