3862996045
CI/CD Pipeline / Check if frontend-only change (push) Waiting to run
CI/CD Pipeline / Validate - Code Quality (push) Waiting to run
CI/CD Pipeline / Validate - Type Check (mypy) (push) Waiting to run
CI/CD Pipeline / Validate - Migration (alembic) (push) Waiting to run
CI/CD Pipeline / Unit Tests (push) Blocked by required conditions
CI/CD Pipeline / Integration Tests (push) Blocked by required conditions
CI/CD Pipeline / Frontend Lint (push) Waiting to run
CI/CD Pipeline / Frontend Unit Tests (push) Blocked by required conditions
CI/CD Pipeline / PR Build API Image (push) Waiting to run
CI/CD Pipeline / PR Build Web Image (push) Waiting to run
CI/CD Pipeline / PR Build Worker Image (push) Waiting to run
CI/CD Pipeline / Build Staging API Image (push) Waiting to run
CI/CD Pipeline / Build Staging Web Image (push) Waiting to run
CI/CD Pipeline / Build Staging Worker Image (push) Waiting to run
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Blocked by required conditions
CI/CD Pipeline / Staging E2E Tests (push) Blocked by required conditions
CI/CD Pipeline / Staging API Integration Tests (push) Blocked by required conditions
CI/CD Pipeline / Build Production API Image (push) Waiting to run
CI/CD Pipeline / Build Production Web Image (push) Waiting to run
CI/CD Pipeline / Build Production Worker Image (push) Waiting to run
CI/CD Pipeline / Deploy Production (push) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (push) Blocked by required conditions
CI/CD Pipeline / ACR Image Cleanup (push) Blocked by required conditions
175 lines
5.2 KiB
TypeScript
175 lines
5.2 KiB
TypeScript
/**
|
|
* 成品 / 视频相关 API
|
|
* 后端实际接口:/videos
|
|
*/
|
|
import apiClient from "./client"
|
|
|
|
/** 复核状态 */
|
|
export type ReviewStatus = "pending_review" | "approved" | "rejected"
|
|
|
|
/** 成品条目 */
|
|
export interface ProductItem {
|
|
id: string
|
|
title: string
|
|
video_url?: string
|
|
thumbnail_url?: string
|
|
duration_seconds?: number
|
|
file_size?: number
|
|
resolution?: string
|
|
status: "processing" | "completed" | "failed"
|
|
/** 复核状态 */
|
|
review_status?: ReviewStatus
|
|
/** 所属项目 ID */
|
|
project_id?: string
|
|
/** 所属项目名称 */
|
|
project_name?: string
|
|
/** 查重率(百分比) */
|
|
duplicate_rate?: number
|
|
created_at?: string
|
|
updated_at?: string
|
|
}
|
|
|
|
/** 列表查询参数 */
|
|
export interface ProductListParams {
|
|
page?: number
|
|
page_size?: number
|
|
project_id?: string
|
|
review_status?: ReviewStatus | "all"
|
|
}
|
|
|
|
/** 分页响应 */
|
|
export interface ProductListResponse {
|
|
items: ProductItem[]
|
|
total: number
|
|
page: number
|
|
page_size: number
|
|
}
|
|
|
|
/** 批量下载任务状态 */
|
|
export interface BatchDownloadStatus {
|
|
job_id: string
|
|
status: "processing" | "completed" | "failed"
|
|
/** 完成后返回的下载 URL */
|
|
download_url?: string
|
|
/** 进度百分比 */
|
|
progress?: number
|
|
}
|
|
|
|
/** 后端 /videos 接口返回的原始视频条目 */
|
|
interface VideoItem {
|
|
id: string
|
|
project_id: string
|
|
generation_task_id: string
|
|
name: string
|
|
file_url: string
|
|
file_size: number
|
|
duration: number
|
|
thumbnail_url: string | null
|
|
width: number
|
|
height: number
|
|
fps: number
|
|
status: string
|
|
review_status: ReviewStatus
|
|
generation_params: Record<string, unknown>
|
|
download_url: string
|
|
generated_at: string
|
|
}
|
|
|
|
/**
|
|
* 将后端 VideoItem 映射为 ProductItem 格式
|
|
*/
|
|
function mapVideoToProductItem(video: VideoItem): ProductItem {
|
|
return {
|
|
id: video.id,
|
|
title: video.name || "未命名视频",
|
|
// 优先用 download_url(带签名)播放,file_url 无签名无法访问
|
|
video_url: video.download_url || video.file_url,
|
|
thumbnail_url: video.thumbnail_url || undefined,
|
|
duration_seconds: video.duration,
|
|
file_size: video.file_size,
|
|
resolution: video.width && video.height ? `${video.width}x${video.height}` : undefined,
|
|
status:
|
|
video.status === "completed"
|
|
? "completed"
|
|
: video.status === "failed"
|
|
? "failed"
|
|
: "processing",
|
|
review_status: video.review_status,
|
|
project_id: video.project_id,
|
|
// 后端 /videos 接口暂无 project_name 字段
|
|
project_name: undefined,
|
|
// 后端字段名为 generated_at,映射为 created_at 供前端统一使用
|
|
created_at: video.generated_at,
|
|
updated_at: video.generated_at,
|
|
// 后端 /videos 接口暂无 duplicate_rate 字段
|
|
duplicate_rate: undefined,
|
|
}
|
|
}
|
|
|
|
/** 获取成品列表(支持分页和筛选) */
|
|
export const getProducts = async (params?: ProductListParams): Promise<ProductItem[]> => {
|
|
const response = await apiClient.get("/videos", { params })
|
|
const data = response.data
|
|
const videos: VideoItem[] = Array.isArray(data?.items)
|
|
? data.items
|
|
: Array.isArray(data)
|
|
? data
|
|
: []
|
|
return videos.map(mapVideoToProductItem)
|
|
}
|
|
|
|
/** 获取单个成品详情 */
|
|
export const getProduct = async (productId: string): Promise<ProductItem> => {
|
|
const response = await apiClient.get(`/videos/${productId}`)
|
|
return mapVideoToProductItem(response.data as VideoItem)
|
|
}
|
|
|
|
/**
|
|
* 删除成品
|
|
* 注意:后端暂未实现 /videos DELETE 接口,调用会返回 405
|
|
* 待后端实现后自动生效
|
|
*/
|
|
export const deleteProduct = async (productId: string): Promise<void> => {
|
|
await apiClient.delete(`/videos/${productId}`)
|
|
}
|
|
|
|
/**
|
|
* 获取成品下载链接
|
|
* 直接使用列表返回的 download_url(带OSS签名)
|
|
*/
|
|
export const getProductDownloadUrl = async (
|
|
productId: string,
|
|
): Promise<{ url: string; expires_at: string }> => {
|
|
// 优先从列表缓存取;如果没有则调详情接口
|
|
const product = await getProduct(productId)
|
|
if (!product.video_url) throw new Error("下载链接不可用")
|
|
return { url: product.video_url, expires_at: "" }
|
|
}
|
|
|
|
/** 更新复核状态 — TODO: 后端暂无对应端点,暂存本地状态 */
|
|
export const updateReviewStatus = async (
|
|
productId: string,
|
|
status: ReviewStatus,
|
|
): Promise<ProductItem> => {
|
|
// 后端暂无 /videos/{id}/review 端点
|
|
// 暂时返回当前状态,后续可扩展
|
|
const product = await getProduct(productId)
|
|
return { ...product, review_status: status }
|
|
}
|
|
|
|
/** 发起批量下载 — TODO: 后端暂无对应端点 */
|
|
export const batchDownload = async (videoIds: string[]): Promise<{ job_id: string }> => {
|
|
// 后端暂无 /videos/batch-download 端点
|
|
// 暂时返回模拟 job_id,后续可扩展
|
|
console.warn("[batchDownload] 后端暂无批量下载端点", videoIds)
|
|
return { job_id: `mock-${Date.now()}` }
|
|
}
|
|
|
|
/** 查询批量下载状态 — TODO: 后端暂无对应端点 */
|
|
export const getBatchDownloadStatus = async (jobId: string): Promise<BatchDownloadStatus> => {
|
|
// 后端暂无 /videos/batch-download/{jobId} 端点
|
|
// 暂时返回模拟状态,后续可扩展
|
|
console.warn("[getBatchDownloadStatus] 后端暂无批量下载状态端点", jobId)
|
|
return { job_id: jobId, status: "processing", progress: 0 }
|
|
}
|