diff --git a/src/api/index.ts b/src/api/index.ts index 76cecb0..62c71b0 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -127,7 +127,7 @@ function buildHttpRequest(cmd: string, args: Record): { url: string return { url: `/api/v1/faces/candidates?page=${a.page || 1}&page_size=${a.perPage || 100}`, method: 'GET' } } case 'get_unassigned_traces': { - let url = `/api/v1/traces/unassigned?page=${a.page || 1}&per_page=${a.perPage || 20}` + let url = `/api/v1/traces/unassigned?page=${a.page || 1}&page_size=${a.perPage || 20}` if (a.fileUuid) url += `&file_uuid=${a.fileUuid}` return { url, method: 'GET' } } @@ -437,16 +437,43 @@ case 'get_unassigned_traces': { } case 'search_llm_smart': { const results = data.results || data.data || data || [] - return results.map((r: any) => ({ - file_uuid: r.file_uuid || '', - start_time: r.start_time ?? 0, - end_time: r.end_time ?? 0, - start_frame: r.start_frame ?? 0, - end_frame: r.end_frame ?? 0, - summary: r.summary || r.raw_text || '', - similarity: r.similarity || 0, - file_name: r.file_name || null, - })) + return results.map((r: any) => { + const asr = r.asr || null + const asrSegments = asr?.segments || [] + const asrLang = asr?.language || '' + const asrLangProb = asr?.language_probability || 0 + + let asrStatus: 'no_audio_track' | 'silent_audio' | 'has_transcript' | 'processing' = 'processing' + let asrMessage = '處理中' + + if (asrSegments.length === 0) { + if (asrLang === '' && asrLangProb === 0) { + asrStatus = 'no_audio_track' + asrMessage = '無音軌' + } else { + asrStatus = 'silent_audio' + asrMessage = asrLang ? `無語音 (${asrLang})` : '無語音' + } + } else { + asrStatus = 'has_transcript' + asrMessage = `${asrSegments.length} 段語音 (${asrLang || 'unknown'})` + } + + return { + file_uuid: r.file_uuid || '', + start_time: r.start_time ?? 0, + end_time: r.end_time ?? 0, + start_frame: r.start_frame ?? 0, + end_frame: r.end_frame ?? 0, + summary: r.summary || r.raw_text || '', + similarity: r.similarity || 0, + file_name: r.file_name || null, + asr_status: asrStatus, + asr_message: asrMessage, + asr_segment_count: asrSegments.length, + asr_language: asrLang, + } + }) } case 'search_identities': { const results = data.results || data.data || data || [] diff --git a/src/utils/asrStatus.ts b/src/utils/asrStatus.ts new file mode 100644 index 0000000..8cd6075 --- /dev/null +++ b/src/utils/asrStatus.ts @@ -0,0 +1,48 @@ +export type AsrStatus = 'no_audio_track' | 'silent_audio' | 'has_transcript' | 'processing' + +export interface AsrData { + language?: string + language_probability?: number + segments?: Array<{ + start_time: number + end_time: number + text: string + }> +} + +export function getAsrStatus(asr: AsrData | null | undefined): AsrStatus { + if (!asr) return 'processing' + + const segments = asr.segments || [] + const language = asr.language || '' + const langProb = asr.language_probability || 0 + + if (segments.length === 0) { + if (language === '' && langProb === 0) { + return 'no_audio_track' + } + return 'silent_audio' + } + + return 'has_transcript' +} + +export function getAsrStatusLabel(status: AsrStatus): string { + const labels: Record = { + 'no_audio_track': '無音軌', + 'silent_audio': '無語音', + 'has_transcript': '有語音', + 'processing': '處理中' + } + return labels[status] +} + +export function getAsrStatusColor(status: AsrStatus): string { + const colors: Record = { + 'no_audio_track': '#999', + 'silent_audio': '#f90', + 'has_transcript': '#0a0', + 'processing': '#59e' + } + return colors[status] +} \ No newline at end of file diff --git a/src/views/SearchView.vue b/src/views/SearchView.vue index c67415d..47cf842 100644 --- a/src/views/SearchView.vue +++ b/src/views/SearchView.vue @@ -118,6 +118,7 @@ {{ r.chunk_id }} F{{ r.start_frame }}–{{ r.end_frame }} {{ r.source }} + {{ r.asr_message }} @@ -1034,6 +1035,11 @@ function openSource(src: SourceInfo) { .card-time { color: #9ca3af; } .card-score { color: #6366f1; font-weight: 500; } .card-source { background: #eef2ff; color: #4f46e5; border-radius: 4px; padding: 1px 5px; font-size: 10px; font-weight: 500; } +.card-asr { border-radius: 4px; padding: 1px 5px; font-size: 10px; font-weight: 500; } +.card-asr--no_audio_track { background: #f3f4f6; color: #6b7280; } +.card-asr--silent_audio { background: #fef3c7; color: #d97706; } +.card-asr--has_transcript { background: #d1fae5; color: #059669; } +.card-asr--processing { background: #dbeafe; color: #2563eb; } .ibar { border-top: 1px solid #e5e7eb;