103 lines
2.6 KiB
TypeScript
103 lines
2.6 KiB
TypeScript
const API_BASE = process.env.NEXT_PUBLIC_API_BASE ?? "http://localhost:4291"
|
|
|
|
export type JobStatus =
|
|
| "created"
|
|
| "downloading"
|
|
| "downloaded"
|
|
| "splitting"
|
|
| "frames_extracted"
|
|
| "transcribing"
|
|
| "transcribed"
|
|
| "failed"
|
|
|
|
export interface KeyFrame {
|
|
index: number
|
|
timestamp: number
|
|
url: string
|
|
}
|
|
|
|
export interface TranscriptSegment {
|
|
index: number
|
|
start: number
|
|
end: number
|
|
en: string
|
|
zh: string
|
|
}
|
|
|
|
export interface Job {
|
|
id: string
|
|
url: string
|
|
status: JobStatus
|
|
progress: number
|
|
message?: string
|
|
video_url?: string
|
|
duration?: number
|
|
width?: number
|
|
height?: number
|
|
frames: KeyFrame[]
|
|
transcript: TranscriptSegment[]
|
|
error?: string
|
|
}
|
|
|
|
export async function createJob(tkUrl: string): Promise<Job> {
|
|
const res = await fetch(`${API_BASE}/jobs`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ url: tkUrl }),
|
|
})
|
|
if (!res.ok) throw new Error(`createJob ${res.status}`)
|
|
return res.json()
|
|
}
|
|
|
|
export async function uploadJob(file: File): Promise<Job> {
|
|
const fd = new FormData()
|
|
fd.append("file", file)
|
|
const res = await fetch(`${API_BASE}/jobs/upload`, {
|
|
method: "POST",
|
|
body: fd,
|
|
})
|
|
if (!res.ok) {
|
|
const text = await res.text().catch(() => "")
|
|
throw new Error(`uploadJob ${res.status} ${text.slice(0, 200)}`)
|
|
}
|
|
return res.json()
|
|
}
|
|
|
|
export async function getJob(id: string): Promise<Job> {
|
|
const res = await fetch(`${API_BASE}/jobs/${id}`)
|
|
if (!res.ok) throw new Error(`getJob ${res.status}`)
|
|
return res.json()
|
|
}
|
|
|
|
export async function triggerTranscribe(id: string): Promise<Job> {
|
|
const res = await fetch(`${API_BASE}/jobs/${id}/transcribe`, { method: "POST" })
|
|
if (!res.ok) throw new Error(`transcribe ${res.status}`)
|
|
return res.json()
|
|
}
|
|
|
|
export async function analyzeJob(id: string, frames = 5): Promise<Job> {
|
|
const res = await fetch(`${API_BASE}/jobs/${id}/analyze?frames=${frames}`, { method: "POST" })
|
|
if (!res.ok) {
|
|
const t = await res.text().catch(() => "")
|
|
throw new Error(`analyze ${res.status} ${t.slice(0, 200)}`)
|
|
}
|
|
return res.json()
|
|
}
|
|
|
|
export async function addManualFrame(id: string, t: number): Promise<Job> {
|
|
const res = await fetch(`${API_BASE}/jobs/${id}/frames?t=${encodeURIComponent(t.toFixed(2))}`, { method: "POST" })
|
|
if (!res.ok) {
|
|
const txt = await res.text().catch(() => "")
|
|
throw new Error(`addFrame ${res.status} ${txt.slice(0, 200)}`)
|
|
}
|
|
return res.json()
|
|
}
|
|
|
|
export function frameUrl(jobId: string, frameIndex: number): string {
|
|
return `${API_BASE}/jobs/${jobId}/frames/${frameIndex}.jpg`
|
|
}
|
|
|
|
export function videoUrl(jobId: string): string {
|
|
return `${API_BASE}/jobs/${jobId}/video.mp4`
|
|
}
|