import { Injectable } from '@angular/core'; import { DouyinTranscriptJob, DouyinTranscriptStatus } from '../models/douyin-insight.model'; @Injectable({ providedIn: 'root' }) export class ViralTranscriptPresenterService { private readonly processingStatuses = new Set([ 'queued', 'resolving_detail', 'selecting_media', 'downloading_media', 'extracting_audio', 'submitting_provider', 'polling_provider', 'pending', ]); private readonly labels: Record = { queued: '排队中', resolving_detail: '获取视频详情', selecting_media: '选择媒体', downloading_media: '下载媒体', extracting_audio: '提取音频', submitting_provider: '提交转写', polling_provider: '等待结果', pending: '处理中', completed: '已完成', failed: '失败', needs_media: '缺少可转写媒体', needs_media_processing: '需要媒体处理环境', needs_provider_config: '服务未配置', }; canStart(analysis: { transcript?: string; transcriptJob?: DouyinTranscriptJob } | null | undefined, busy: boolean): boolean { if (!analysis || busy || !!analysis.transcript) return false; const status = analysis.transcriptJob?.status; return !status || status === 'failed' || status === 'needs_media' || status === 'needs_media_processing' || status === 'needs_provider_config'; } statusLabel(job: DouyinTranscriptJob | undefined): string { if (!job) return '未提交'; return this.labels[job.status] || job.stageLabel || '未知状态'; } primaryMessage(job: DouyinTranscriptJob | undefined): string { if (!job) return ''; return this.message(job); } isProcessing(job: DouyinTranscriptJob | undefined): boolean { return !!job && this.processingStatuses.has(job.status); } shouldWarn(job: DouyinTranscriptJob | undefined): boolean { return !!job && job.status !== 'completed' && !this.isProcessing(job); } toastType(job: DouyinTranscriptJob): 'success' | 'warn' { return this.isProcessing(job) || job.status === 'completed' ? 'success' : 'warn'; } message(job: DouyinTranscriptJob): string { if (job.stageLabel && this.isProcessing(job)) return job.stageLabel; if (job.status === 'pending' || job.status === 'queued') return '逐字稿任务已提交,稍后可刷新结果'; if (job.status === 'completed') return '逐字稿已回填'; if (job.status === 'needs_media') return '未找到可转写媒体,请确认视频详情能返回音频或视频地址'; if (job.status === 'needs_media_processing') return job.errorMessage || '需要可用的媒体处理环境,请确认本地 ffmpeg 可用'; if (job.status === 'needs_provider_config') { return job.warnings?.[0] || '未读取到转写服务配置,请检查 VOC_TOKEN、TRANSCRIPTION_VOC_TOKEN 或 VOICE_TOKEN'; } return job.errorMessage || job.warnings?.[0] || '逐字稿任务失败,可修复配置后重新提交'; } }